赞
踩
目录
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈在逻辑上仍然是线性的,但是与顺序表、链表不同,栈只能在栈顶入数据,在栈底出数据,不能在任意位置进行操作,栈顶与栈底是根据功能命名的,没有严格规定首部是栈顶,尾部是栈底。
在数据结构之前的学习中,我们已经学习了链式与数组这两种存储的结构,栈的实现一般都可以使用数组或者链表实现。相对而言数组的结构实现更优一些。因为数组在尾上插入数据的 代价比较小。
对于数组而言,如果我们将头部作为栈底,那么压栈出栈就需要挪动许多数据,算法复杂度为较大,因此我们一般将数组头部作为栈顶,尾部作为栈底.
与数组不同,对于单链表而言,如果我们将链表的尾部作为栈底,我们是无法直接访问链表尾部的,同时进行压栈、出栈时,我们还需要找到尾节点的前一个节点,这样使用链表构建栈就会相当麻烦,读者读到这里,可能会认为找前一个节点麻烦,这是由于单链表结构的固有缺陷导致的,那我们直接使用双向链表,不就好了吗?确实,找前一个节点不麻烦了,但是为了解决这个问题,使用了双向链表,那么我们每个节点就多出一个需要维护的节点,空间损耗就大了,而且找尾节点还是需要遍历的,总得来说,我们花费的代价就太大了。因此将链表的尾部作为栈底并不是一个明智的选择。因此我们还是使用单链表(不带头),只不过将单链表第一个有效节点处,作为栈顶。这样我们进行压栈、出栈就方便了。
数组与链表两种结构看上去各有千秋,不过,实际上有数组去实现栈会更优一些。有的读者可能会认为如果是动态开辟数组的话,不是会有扩容上的消耗吗?同时不是还有将数据迁移到新的空间上的消耗吗?确实,如果仅看这些,按需申请的链表似乎更优一些,但是扩容调用的是系统上的资源,运行很快,并且一次扩容都是按倍扩,扩容的次数并不会很多,更重要的是数组的CPU的高速缓存更好(涉及知识点较多,这里便不展开了),因此,这里笔者主要使用数组来实现栈。
静态的结构在实践用到的地方并不多,因此我们实现动态增长的栈。栈的结构体中,_a指向动态开辟的数组空间,_top用来指向栈顶元素或者栈顶元素的下一个位置,_capacity用来表示栈的容量
- // 支持动态增长的栈
- typedef int STDataType;
-
- typedef struct Stack
- {
- STDataType* _a;
- int _top; // 栈顶
- int _capacity; // 容量
- }Stack;
栈的初始化中,需要注意的是_top的初始值初始化为-1与初始化为0是截然不同的。我们的惯性思考会认为没有数据就是0,但是_top此处并不是单纯的表示数据个数的,我们知道数组能访问到最小下表是0,如果_top是表示指向栈顶数据的,那么当_top初始化为0,表示没有数据,当数组内有一个数据时,为了表示有一个数据,我们就将_top+1吗?这样似乎浪费空间了,因此如果_top是表示指向栈顶数据,我们就将_top初始化为-1,如果_top指向栈顶数据的下一个位置,我们就将_top始化为 0,这时的_top就还可以表示表示数组元素的个数了。笔者接下来实现栈的结构,_top指向栈顶元素的下一个位置。
- // 初始化栈
- void StackInit(Stack* ps)
- {
- assert(ps);
-
- ps->_a = NULL;
-
- // top指向栈顶数据的下一个位置
- ps->_top = 0;
-
- // top指向栈顶数据
- //ps->_top = -1;
-
- ps->_capacity = 0;
- }
- // 销毁栈
- void StackDestroy(Stack* ps)
- {
- assert(ps);
-
- free(ps->_a);
- ps->_a = NULL;
- ps->_capacity = ps->_top = 0;
- }
入栈之前,我们需要先判断数组的空间是否足够,如果不足,我们再扩容,因为_top指向的是栈顶元素的下一个数据,一次插入时的_top当前指向的位置就是要插入数据的位置。之后_top再加一。
- // 入栈
- void StackPush(Stack* ps, STDataType data)
- {
- assert(ps);
-
- //扩容
- if(ps->_capacity == ps->_top)
- {
- //对于第一次开辟空间,先初始化4个字节空间,之后每次扩容,扩大两倍
- int newcapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
- STDataType* newnode = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
-
- if (NULL == newnode)
- {
- perror("StackInit:realloc");
- exit(1);
- }
-
- ps->_a = newnode;
- //更新容量的记录值
- ps->_capacity = newcapacity;
- }
-
- ps->_a[ps->_top] = data;
- ps->_top++;
- }
- // 出栈
- void StackPop(Stack* ps)
- {
-
- assert(ps);
- assert(ps->_top > 0);
-
- ps->_top--;
-
- }
- // 获取栈顶元素
- STDataType StackTop(Stack* ps)
- {
-
- assert(ps);
- assert(ps->_top > 0);
-
- return ps->_a[ps->_top - 1];
- }
- // 获取栈中有效元素个数
- int StackSize(Stack* ps)
- {
- assert(ps);
-
- return ps->_top;
- }
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- bool StackEmpty(Stack* ps)
- {
- assert(ps);
-
- return ps->_top == 0;
- }
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据时,就需要挪动数据,效率会比较低。
另外扩展了解一下,实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型 时可以就会使用循环队列(涉及到许多其他知识,这里便不过多介绍)。环形队列可以使用数组实现,也可以使用循环链表实现。
- typedef int QDataType;
-
- // 链式结构:表示队列
- typedef struct QListNode
- {
- struct QListNode* _next;
- QDataType _data;
- }QNode;
与栈不同,在实现队列时,以入队列为例,我们需要传尾指针,如果要改变尾节点还要传对应的二级指针,同时如果队列现在一个节点都没有,我们还需要传对应头指针的二级指针。总得来说,各种接口的使用似乎非常不便,针对这种问题,我们专门创建一个结构体去维护头指针与尾指针。调用接口时,我们只需要传入这个结构体的指针就可以了。这样我们就可以通过访问结构体来改变对应的头尾指针。此外加上_size记录数据个数
- // 队列的结构
- typedef struct Queue
- {
- QNode* _front;
- QNode* _rear;
- int _size;
- }Queue;
传入专门维护的结构体初始化。
- // 初始化队列
- void QueueInit(Queue* q)
- {
- q->_front = NULL;
-
- q->_rear = NULL;
-
- q->_size = 0;
- }
循环遍历节点,挨个释放
- // 销毁队列
- void QueueDestroy(Queue* q)
- {
- assert(q);
-
- QNode* cur = q->_front;
- while(cur)
- {
- QNode* next = cur->_next;
- free(cur);
-
- cur = next;
- }
-
- q->_front = q->_rear = NULL;
- q->_size = 0;
- }
如果队列内没有节点时,在入队列时,还需要该表头结点。
- // 队尾入队列
- void QueuePush(Queue* q, QDataType data)
- {
- assert(q);
-
- QNode* newnode = (QNode*)malloc(sizeof(QNode));
-
- if (NULL == newnode)
- {
- perror("QueuePush:malloc failed");
- exit(1);
- }
-
- newnode->_data = data;
- newnode->_next = NULL;
-
- if (NULL == q->_rear)
- {
- q->_front = q->_rear = newnode;
- }
-
- else
- {
- q->_rear->_next = newnode;
- q->_rear = newnode;
- }
-
- q->_size++;
- }
出队列之前需要判断是否队列中是否存在数据,没有数据,无法出数据。如果队列队列中只有一个数据,出完数据,还需要改变头结点。
- // 队头出队列
- void QueuePop(Queue* q)
- {
- assert(q);
- assert(q->_size);
-
- QNode* next = q->_front->_next;
- free(q->_front);
- q->_front = next;
-
- if (q->_size == 1)
- {
- q->_rear = NULL;
- }
-
- q->_size--;
-
- }
首先判断头指针是否为空,不为空才可以获取头部元素。
- // 获取队列头部元素
- QDataType QueueFront(Queue* q)
- {
- assert(q);
- assert(q->_front);
-
- return q->_front->_data;
- }
首先判断尾指针是否为空,不为空才可以获取尾部元素。
- // 获取队列队尾元素
- QDataType QueueBack(Queue* q)
- {
- assert(q);
- assert(q->_rear);
-
- return q->_rear->_data;
-
- }
- // 获取队列中有效元素个数
- int QueueSize(Queue* q)
- {
- assert(q);
-
- return q->_size;
- }
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- bool QueueEmpty(Queue* q)
- {
- assert(q);
-
- return q->_size == 0;
- }
这一道题,要求左括号与对应的右括号匹配,当取出字符串中的右括号时,需要与当前右括号的前面第一个左括号括号比对,如果是对应的左括号,则匹配成功,取下一个右括号,并且取前面第一个左括号匹配(已经匹配的左括号不再参与匹配)。我们发现我们总是需要取离右括号最近的左括号(相对其他左括号顺序靠后的元素)。匹配后,已匹配的括号不参加,下次匹配据需向“左找”。
因此根据题意,我们这里可以创建栈,循环遍历字符数组,如果当前是左括号,则入栈,如果是右括号,那么这时,我们就取栈顶的元素(由于栈是后入先出栈,因此栈顶的元素就是左边离右括号最近的左括号),与右括号匹配,如果匹配则,将匹配的左括号出栈,继续循环,如果不匹配,则结束遍历。
- #include<stdlib.h>
- #include<stdbool.h>
- #include<assert.h>
- #include<stdio.h>
-
-
- // 支持动态增长的栈
- typedef int STDataType;
-
- typedef struct Stack
- {
- STDataType* _a;
- int _top; // 栈顶
- int _capacity; // 容量
- }Stack;
-
- // 初始化栈
- void StackInit(Stack* ps)
- {
- ps->_a = NULL;
- ps->_capacity = ps->_top = 0;
- }
-
- // 入栈
- void StackPush(Stack* ps, STDataType data)
- {
- assert(ps);
- if(ps->_capacity == ps->_top)
- {
- int newcapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
- STDataType* newnode = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
-
- if (NULL == newnode)
- {
- perror("StackInit:realloc");
- exit(1);
- }
- ps->_a = newnode;
- ps->_capacity = newcapacity;
- }
-
- ps->_a[ps->_top] = data;
- ps->_top++;
- }
-
- // 出栈
- void StackPop(Stack* ps)
- {
-
- assert(ps);
- assert(ps->_top > 0);
-
- ps->_top--;
-
- }
-
- // 获取栈顶元素
- STDataType StackTop(Stack* ps)
- {
-
- assert(ps);
- assert(ps->_top > 0);
-
- return ps->_a[ps->_top - 1];
- }
-
- // 获取栈中有效元素个数
- int StackSize(Stack* ps)
- {
- assert(ps);
-
- return ps->_top;
- }
-
- // 检测栈是否为空,如果为空返回0,如果不为空返回非零结果
- bool StackEmpty(Stack* ps)
- {
- assert(ps);
- return ps->_top;
- }
-
- // 销毁栈
- void StackDestroy(Stack* ps)
- {
- assert(ps);
- free(ps->_a);
- ps->_a = NULL;
- ps->_capacity = ps->_top = 0;
- }
-
-
- bool isValid(char* s)
- {
- Stack p;
- StackInit(&p);
- while(*s)
- {
- if(*s == '(' || *s == '{' || *s == '[')//左括号入栈
- {
- StackPush(&p,*s);
- }
- else
- {
- if(!StackEmpty(&p))//右括号入栈前不能没有左括号,否则一定会出现不匹配的情况
- {
- StackDestroy(&p);
- return false;
- }
-
- char top = 0;
- top = StackTop(&p);
-
- if(top == '(' && *s != ')'||top == '{' && *s != '}'||top == '[' && *s != ']')//括号不匹配返回false
- {
- StackDestroy(&p);
- return false;
- }
-
- StackPop(&p);//匹配成功,将匹配的左括号出栈
- }
-
- s++;//移动到下一个括号
- }
-
- int ret = !StackEmpty(&p);//判断栈内是否为空,为空说明存在未匹配的左括号
- StackDestroy(&p);
-
- return ret;
- }
这一题,我们需要根据队列先进先出的性质实现栈后进先出的性质,题目给了我们两个队列,因此这题就是通过将数据在两个列类之间转移来完成的,问题的关键就在于如何转移数据。
如果说队列中已经有数据,为了出4,这是我们就可以将1、2、3入队列入到q2中,这时我们就可以出q1中的4,达到后进先出的效果,之后加入我们入栈5,6呢?我们又将5,6入到哪个队列中呢?
假如们入到 q1,中,这时如果我们将5入到q2,再出6,似乎没什么问题,那么这是如果我们不出6,我们还需要继续导入数据呢?
这是我们发现,面对两个都不为空的队列,我们再想入数据,我们必须先遍历已经存储的数据进行,判断当数据较多时,我们就会发现复杂度以及程序的逻辑性都不会太好。因此,为了避免这一混乱的情况发生,我们必须保证将数据入到不为空的队列,这样出数据将数据导到空队列中,出完数量后,就又保持两队列一空一不为空了。
- #include<stdio.h>
- #include<assert.h>
- #include<stdlib.h>
- #include"stdbool.h"
-
- typedef int QDataType;
-
- // 链式结构:表示队列
- typedef struct QListNode
- {
- struct QListNode* _next;
- QDataType _data;
- }QNode;
-
- // 队列的结构
- typedef struct Queue
- {
- QNode* _front;
- QNode* _rear;
- int _size;
- }Queue;
- void QueueInit(Queue* q)
- {
- q->_front = NULL;
-
- q->_rear = NULL;
-
- q->_size = 0;
- }
-
- // 队尾入队列
- void QueuePush(Queue* q, QDataType data)
- {
- assert(q);
- QNode* newnode = (QNode*)malloc(sizeof(QNode));
-
- if (NULL == newnode)
- {
- perror("QueuePush:malloc failed");
- exit(1);
- }
-
- newnode->_data = data;
- newnode->_next = NULL;
-
- if (0 == q->_size)
- {
- q->_front = q->_rear = newnode;
- }
- else
- {
- q->_rear->_next = newnode;
- q->_rear = q->_rear->_next;
- }
- q->_size++;
- }
-
- // 队头出队列
- void QueuePop(Queue* q)
- {
- assert(q);
- assert(q->_size);
-
- QNode* next = q->_front->_next;
- free(q->_front);
- q->_front = next;
-
- if (q->_size == 1)
- {
- q->_rear = NULL;
- }
-
- q->_size--;
-
- }
-
- // 获取队列头部元素
- QDataType QueueFront(Queue* q)
- {
- assert(q);
- assert(q->_size);
-
- return q->_front->_data;
- }
-
- // 获取队列队尾元素
- QDataType QueueBack(Queue* q)
- {
- assert(q);
- assert(q->_size);
-
- return q->_rear->_data;
-
- }
-
- // 获取队列中有效元素个数
- int QueueSize(Queue* q)
- {
- assert(q);
-
- return q->_size;
- }
-
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- bool QueueEmpty(Queue* q)
- {
- assert(q);
-
- return q->_size == 0;
- }
-
- // 销毁队列
- void QueueDestroy(Queue* q)
- {
- assert(q);
-
- QNode* cur = q->_front;
- while(cur)
- {
- QNode* next = cur->_next;
- free(cur);
-
- cur = next;
- }
-
- q->_front = q->_rear = NULL;
- q->_size = 0;
- }
- typedef struct {
- Queue q1;
- Queue q2;
- } MyStack;
-
-
- MyStack* myStackCreate() {
- MyStack*pst = (MyStack*)malloc(sizeof(MyStack));
- //创建模拟的栈时,只能malloc动态创建,不能静态开辟,否则出了函数,开辟空间会被收回
- //也不能使用static, 否则多次调用函数,值会一直保存,会出问题。
- if(NULL == pst)
- {
- perror("malloc");
- exit(1);
- }
-
- QueueInit(&pst->q1);
- QueueInit(&pst->q2);
-
- return pst;
- }
-
- void myStackPush(MyStack* obj, int x) {
- if(!QueueEmpty(&obj->q1))
- {
- QueuePush(&obj->q1,x);
- }
-
- else
- {
- QueuePush(&obj->q2,x);
- }
- }
-
- int myStackPop(MyStack* obj) {
- //假设法
- Queue* empty = &(obj->q1);
- Queue* nonempty = &(obj->q2);
- if(!QueueEmpty(&(obj->q1)))
- {
- nonempty = &(obj->q1);
- empty = &(obj->q2);
- }
-
- //不为空的队列前size-1导走,删除最后一个就是栈顶数据
- while(QueueSize(nonempty) > 1)
- {
- QueuePush(empty,QueueFront(nonempty));
- QueuePop(nonempty);
- }
-
- int top = QueueFront(nonempty);
- QueuePop(nonempty);
-
- return top;
- }
-
- int myStackTop(MyStack* obj) {
- if(!QueueEmpty(&(obj->q1)))
- {
- return QueueBack(&(obj->q1));
- }
- else
- {
- return QueueBack(&(obj->q2));
- }
- }
-
- bool myStackEmpty(MyStack* obj) {
- //两个队列都为空才是空
- return QueueEmpty(&(obj->q1))&&QueueEmpty(&(obj->q2));
- }
-
- void myStackFree(MyStack* obj) {
-
- QueueDestroy(&(obj->q1));
- QueueDestroy(&(obj->q2));
-
- free(obj);
- }
-
- /**
- * Your MyStack struct will be instantiated and called as such:
- * MyStack* obj = myStackCreate();
- * myStackPush(obj, x);
-
- * int param_2 = myStackPop(obj);
-
- * int param_3 = myStackTop(obj);
-
- * bool param_4 = myStackEmpty(obj);
-
- * myStackFree(obj);
- */
需要注意的是最后销毁时,要先销毁队列对应的空间,在销毁存储两个队列指针的空间。
对于这一题,如果我们沿用用队列实现栈的思路,似乎也可以,我们将先将其他数据入到另一个栈中,再留下要出栈的数据出栈。但是我们要入栈时,我们不能在入到非空的栈中,因为这样,出数据的时候,顺序就完全乱了,对此,我们可以将原数据再倒回原数据中,再向非空栈中入数据。这样就没问题了,但是这样就比较麻烦了。
根据观察,我们发现其实在将数据到到空栈是,原数据顺序会倒过来,这是我们再出栈,就直接实现了先进先出的效果。
因此我们将两个栈分成一个专门出数据,一个专门入数据,当出数据栈popst为空,我们就将入数据栈pushst数据全部导到popst中(必须一次性将所有数据全部导入,否则顺序就乱了。)入数据时,直接往对应栈入就行了。
- #include<stdlib.h>
- #include<stdbool.h>
- #include<assert.h>
- #include<stdio.h>
-
-
- // 支持动态增长的栈
- typedef int STDataType;
-
- typedef struct Stack
- {
- STDataType* _a;
- int _top; // 栈顶
- int _capacity; // 容量
- }Stack;
- // 初始化栈
- void StackInit(Stack* ps)
- {
- ps->_a = NULL;
- ps->_capacity = ps->_top = 0;
- }
-
- // 入栈
- void StackPush(Stack* ps, STDataType data)
- {
- assert(ps);
- if(ps->_capacity == ps->_top)
- {
- int newcapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
- STDataType* newnode = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
-
- if (NULL == newnode)
- {
- perror("StackInit:realloc");
- exit(1);
- }
- ps->_a = newnode;
- ps->_capacity = newcapacity;
- }
-
- ps->_a[ps->_top] = data;
- ps->_top++;
- }
-
- // 出栈
- void StackPop(Stack* ps)
- {
-
- assert(ps);
- assert(ps->_top > 0);
-
- ps->_top--;
-
- }
-
- // 获取栈顶元素
- STDataType StackTop(Stack* ps)
- {
-
- assert(ps);
- assert(ps->_top > 0);
-
- return ps->_a[ps->_top - 1];
- }
-
- // 获取栈中有效元素个数
- int StackSize(Stack* ps)
- {
- assert(ps);
-
- return ps->_top;
- }
-
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- bool StackEmpty(Stack* ps)
- {
- assert(ps);
- return ps->_top == 0;
- }
-
- // 销毁栈
- void StackDestroy(Stack* ps)
- {
- assert(ps);
- free(ps->_a);
- ps->_a = NULL;
- ps->_capacity = ps->_top = 0;
- }
-
- typedef struct {
- Stack pushst;
- Stack popst;
- } MyQueue;
-
-
- MyQueue* myQueueCreate() {
- MyQueue* q1 = (MyQueue*)malloc(sizeof(MyQueue));
- StackInit(&q1->pushst);
- StackInit(&q1->popst);
- return q1;
- }
-
- void myQueuePush(MyQueue* obj, int x) {
- //实现入队列数据
- StackPush(&obj->pushst,x);
- }
-
-
- int myQueuePeek(MyQueue* obj) {
- if(StackEmpty(&obj->popst))
- {
- //倒数据
- //实现出队列数据
- while(!StackEmpty(&obj->pushst))//专门出数据的栈为空,就先导入一下数据
- {
- StackPush(&obj->popst,StackTop(&obj->pushst));
- StackPop(&obj->pushst);
- }
-
- }
-
- return StackTop(&obj->popst);
- }
-
- int myQueuePop(MyQueue* obj) {
- //出队列队头数据
- int front = myQueuePeek(obj);
- StackPop(&obj->popst);
-
- return front;
-
- }
-
- bool myQueueEmpty(MyQueue* obj) {
- //两个栈都为空,队列才为空
- return StackEmpty(&obj->pushst)&&StackEmpty(&obj->popst);
- }
-
- void myQueueFree(MyQueue* obj) {
- //先释放对应栈空间,再释放队列对应空间
- StackDestroy(&obj->pushst);
- StackDestroy(&obj->popst);
- free(obj);
- }
-
- /**
- * Your MyQueue struct will be instantiated and called as such:
- * MyQueue* obj = myQueueCreate();
- * myQueuePush(obj, x);
-
- * int param_2 = myQueuePop(obj);
-
- * int param_3 = myQueuePeek(obj);
-
- * bool param_4 = myQueueEmpty(obj);
-
- * myQueueFree(obj);
- */
这道题目的意思就是队列的的空间大小是固定的,这些空间可以重复使用,看到这一题的循环结构,也许有的读者会想到链表,认为链表的结构完成这一题会比数组简单,但是实际上并不是这样的,笔者这里先以数组完成这题,链表的不便后文讲解。
首先假设要插入的空间K是4,这是如果我们开辟4个空间,head、tail都初始化为0,push一个数据,tail++,pop一个数据,head++,当tail、head值大于K,通过%K,实现回环,这样head == tail时,就代表数组为空的情况了,但是,我们发现如果数组满时也会出现head == tail,这就是我们说的假溢出的情况了。
为了解决这个问题,笔者有两种方法,一种是创建size专门记录数组元素个数;还有一种是多开一个空间。笔者接下来重点介绍第二种(对于这类问题一些官方解答更喜欢用第二种方法)。
如上图,其他不变,只是我们判断数组满到条件变成了(tail+1)%(k+1) == head;这是为什么呢?我们知道,head是指向队列的头,tail指向尾的下一位,因为我们多开了一个空间,并且是按序的空出空间,因此,当数组满时,尾的空间后面就是空空间,tail指向它,这时如果tail再向前走一步,就与head相等,不过这时,为了避免出现越界的情况,tail+1需要%(K+1),这就相当于tail+1>5时,tail+1-5,tail回到数组开头,同时数组越界的情况是相对与数组总长的,因此这里是%(k+1)不是%k.
-
-
- typedef struct {//创建结构体存储需要用到的值方便维护
- int k;
- int head;
- int tail;
- int* a;
- } MyCircularQueue;
-
-
- MyCircularQueue* myCircularQueueCreate(int k) {//初始化结构体
-
- MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
- obj->a = (int*)malloc((k + 1) * sizeof(int));
- obj->head = 0;
- obj->tail = 0;
- obj->k = k;
-
- return obj;
- }
-
- bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
-
- return obj->head == obj->tail;//head == tail为空
-
- }
-
- bool myCircularQueueIsFull(MyCircularQueue* obj) {
-
- return (obj->tail + 1)%(obj->k + 1) == obj->head;//(tail + 1)%(k + 1) == head 为满
-
- }
-
- bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
-
- if(myCircularQueueIsFull(obj))
- {
- return false;
- }
-
- else
- {
- obj->a[obj->tail] = value;
- obj->tail = (obj->tail + 1) % (obj->k + 1);//防止越界
- return true;
- }
- }
-
- bool myCircularQueueDeQueue(MyCircularQueue* obj) {
-
- if(myCircularQueueIsEmpty(obj))
- {
- return false;
- }
-
- obj->head = (obj->head + 1) % (obj->k + 1);//防止head越界
- return true;
-
- }
-
- int myCircularQueueFront(MyCircularQueue* obj) {
-
- if(myCircularQueueIsEmpty(obj))//为空取不到队头的数据
- {
- return -1;
- }
- else
- {
- return obj->a[obj->head];
- }
- }
-
- int myCircularQueueRear(MyCircularQueue* obj) {
-
- if(myCircularQueueIsEmpty(obj))//为空取不到队尾的数据
- {
- return -1;
- }
- else
- {
- return obj->a[(obj->tail-1 + obj->k + 1) % (obj->k + 1)];
- }
- }
-
-
-
- void myCircularQueueFree(MyCircularQueue* obj) {
-
- free(obj->a);
- free(obj);
-
- }
-
- /**
- * Your MyCircularQueue struct will be instantiated and called as such:
- * MyCircularQueue* obj = myCircularQueueCreate(k);
- * bool param_1 = myCircularQueueEnQueue(obj, value);
-
- * bool param_2 = myCircularQueueDeQueue(obj);
-
- * int param_3 = myCircularQueueFront(obj);
-
- * int param_4 = myCircularQueueRear(obj);
-
- * bool param_5 = myCircularQueueIsEmpty(obj);
-
- * bool param_6 = myCircularQueueIsFull(obj);
-
- * myCircularQueueFree(obj);
- */
这里需要特别注意的是,当我们取队尾的数据时,由于tail是指向队尾数据的下一位,因此如果tail在数组头,tail-1取队尾就会出现负数的情况,因此与(tail+1)%(k+1)类似,我们要将(tail+1 +k+1)%(k+1),这样就可以避免出现,负数的情况,如果tail不在队头这样处理也不会有问题。
那么回到一开始的问题,为什么笔者说,使用链表不像看上去那样简单呢?首先,用链表形成循环队列,不像数组那样直接申请一块空间方便,其次由于tail是指向尾的下一个位置,因此我们如果想要找尾会很麻烦,对此可以使用双向链表、遍历寻找尾、专门创建变量记录tail前一个节点,总的来说,使用链表不光没有什么实质的好处,反而还会有不少麻烦。当然,如果读者想要用链表完成,也不无不可。
1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出栈的顺序是( )。
A 12345ABCDE
B EDCBA54321
C ABCDE12345
D 54321EDCBA
2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是()
A 1,4,3,2
B 2,3,4,1
C 3,1,4,2
D 3,4,2,1
3.循环队列的存储空间为 Q(1:100) ,初始状态为 front=rear=100 。经过一系列正常的入队与退队操作 后, front=rear=99 ,则循环队列中的元素个数为( )
A 1
B 2
C 99
D 0或者100
4.以下( )不是队列的基本运算?
A 从队尾插入一个新元素
B 从队列中删除第i个元素
C 判断一个队列是否为空
D 读取队头元素的值
5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为?(假设 队头不存放数据)
A (rear - front + N) % N + 1
B (rear - front + N) % N
C ear - front) % (N + 1)
D (rear - front + N) % (N - 1)
1.B //依次入栈,入完再出栈,后入先出
2.C //不可能出现2不出,就出到1
3.D //属于没有多开一个空间的方法,相等时可能为空,可能满。
4.B
5.B
- #pragma once
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdlib.h>
- #include<stdbool.h>
- #include<assert.h>
- #include<stdio.h>
-
-
- // 支持动态增长的栈
- typedef int STDataType;
-
- typedef struct Stack
- {
- STDataType* _a;
- int _top; // 栈顶
- int _capacity; // 容量
- }Stack;
-
- // 初始化栈
- void StackInit(Stack* ps);
-
- // 入栈
- void StackPush(Stack* ps, STDataType data);
-
- // 出栈
- void StackPop(Stack* ps);
-
- // 获取栈顶元素
- STDataType StackTop(Stack* ps);
-
- // 获取栈中有效元素个数
- int StackSize(Stack* ps);
-
- // 检测栈是否为空,如果为空返回0 ,如果不为空返回非零结果
- bool StackEmpty(Stack* ps);
-
- // 销毁栈
- void StackDestroy(Stack* ps);
- #include"Stack.h"
-
- // 初始化栈
- void StackInit(Stack* ps)
- {
- assert(ps);
-
- ps->_a = NULL;
-
- // top指向栈顶数据的下一个位置
- ps->_top = 0;
-
- // top指向栈顶数据
- //ps->_top = -1;
-
- ps->_capacity = 0;
- }
-
- // 入栈
- void StackPush(Stack* ps, STDataType data)
- {
- assert(ps);
-
- //扩容
- if(ps->_capacity == ps->_top)
- {
- int newcapacity = ps->_capacity == 0 ? 4 : 2 * ps->_capacity;
- STDataType* newnode = (STDataType*)realloc(ps->_a, newcapacity * sizeof(STDataType));
-
- if (NULL == newnode)
- {
- perror("StackInit:realloc");
- exit(1);
- }
-
- ps->_a = newnode;
- ps->_capacity = newcapacity;
- }
-
- ps->_a[ps->_top] = data;
- ps->_top++;
- }
-
- // 出栈
- void StackPop(Stack* ps)
- {
-
- assert(ps);
- assert(ps->_top > 0);
-
- ps->_top--;
-
- }
-
- // 获取栈顶元素
- STDataType StackTop(Stack* ps)
- {
-
- assert(ps);
- assert(ps->_top > 0);
-
- return ps->_a[ps->_top - 1];
- }
-
- // 获取栈中有效元素个数
- int StackSize(Stack* ps)
- {
- assert(ps);
-
- return ps->_top;
- }
-
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- bool StackEmpty(Stack* ps)
- {
- assert(ps);
-
- return ps->_top == 0;
- }
-
- // 销毁栈
- void StackDestroy(Stack* ps)
- {
- assert(ps);
-
- free(ps->_a);
- ps->_a = NULL;
- ps->_capacity = ps->_top = 0;
- }
- #pragma once
- #define _CRT_SECURE_NO_WARNINGS 1
- #include<stdio.h>
- #include<assert.h>
- #include<stdlib.h>
- #include"stdbool.h"
-
- typedef int QDataType;
-
- // 链式结构:表示队列
- typedef struct QListNode
- {
- struct QListNode* _next;
- QDataType _data;
- }QNode;
-
- // 队列的结构
- typedef struct Queue
- {
- QNode* _front;
- QNode* _rear;
- int _size;
- }Queue;
-
- // 初始化队列
- void QueueInit(Queue* q);
-
- // 队尾入队列
- void QueuePush(Queue* q, QDataType data);
-
- // 队头出队列
- void QueuePop(Queue* q);
-
- // 获取队列头部元素
- QDataType QueueFront(Queue* q);
-
- // 获取队列队尾元素
- QDataType QueueBack(Queue* q);
-
- // 获取队列中有效元素个数
- int QueueSize(Queue* q);
-
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- bool QueueEmpty(Queue* q);
-
- // 销毁队列
- void QueueDestroy(Queue* q);
- #include"queue.h"
-
-
- // 初始化队列
- void QueueInit(Queue* q)
- {
- q->_front = NULL;
-
- q->_rear = NULL;
-
- q->_size = 0;
- }
-
- // 队尾入队列
- void QueuePush(Queue* q, QDataType data)
- {
- assert(q);
-
- QNode* newnode = (QNode*)malloc(sizeof(QNode));
-
- if (NULL == newnode)
- {
- perror("QueuePush:malloc failed");
- exit(1);
- }
-
- newnode->_data = data;
- newnode->_next = NULL;
-
- if (NULL == q->_rear)
- {
- q->_front = q->_rear = newnode;
- }
-
- else
- {
- q->_rear->_next = newnode;
- q->_rear = newnode;
- }
-
- q->_size++;
- }
-
- // 队头出队列
- void QueuePop(Queue* q)
- {
- assert(q);
- assert(q->_size);
-
- QNode* next = q->_front->_next;
- free(q->_front);
- q->_front = next;
-
- if (q->_size == 1)
- {
- q->_rear = NULL;
- }
-
- q->_size--;
-
- }
-
- // 获取队列头部元素
- QDataType QueueFront(Queue* q)
- {
- assert(q);
- assert(q->_front);
-
- return q->_front->_data;
- }
-
- // 获取队列队尾元素
- QDataType QueueBack(Queue* q)
- {
- assert(q);
- assert(q->_rear);
-
- return q->_rear->_data;
-
- }
-
- // 获取队列中有效元素个数
- int QueueSize(Queue* q)
- {
- assert(q);
-
- return q->_size;
- }
-
- // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
- bool QueueEmpty(Queue* q)
- {
- assert(q);
-
- return q->_size == 0;
- }
-
- // 销毁队列
- void QueueDestroy(Queue* q)
- {
- assert(q);
-
- QNode* cur = q->_front;
- while(cur)
- {
- QNode* next = cur->_next;
- free(cur);
-
- cur = next;
- }
-
- q->_front = q->_rear = NULL;
- q->_size = 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。