C语言循环队列与用队列实现栈问题解析
作者:_奇奇
循环队列
循环队列: 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环循环队列的好处:可以重新利用队列的空间。我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
题目描述
设计你的循环队列实现。
你的实现应该支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
题目链接
思路分析
循环队列和普通队列对比。
循环队列:入队需要尾插。出队需要头删,删除并不是真正的删除,只需要使头指针往后移动就可以了,因为要重复利用其空间。真正意义上只需要尾插罢了。尾插的话链表和顺序表时间复杂度相同。综上所述:所以循环队列用顺序表或者链表实现都可以,差异不大。要真正的谁更优,因为顺序表物理空间是连续的,CPU缓存命中率高。所以顺序表更好一点。
普通队列:入队需要尾插,出队需要头删,头删需要真正的删除,但是顺序表头删后还需要覆盖,效率低,所以用单链表实现。
思路 :
1.创建循环队列结构体,包含一个顺序表a,头指针和尾指针head和tail,队列的长度k。
2.要为队列多开一个空间,这样可以正确判断队列是否为空,或者是否满了。红色的空间是多开的一个空间。
3.循环队列的关键在于判断队列是否为空或者队列是否满了。为空:只有当tail == head才为空。
满了:分两种情况。情况1.当tail == 队列长度(k) && head == 0时
情况2:当tail+1 == head时
代码实现
代码写好后。经过我数十次的调试,bug终于调完。
说一说我遇到的bug:
1.第一次提交发现循环队列的创建失败。原因是没有对循环队列的结构体进行初始化。
2.在获取尾部元素的时候报错。漏掉了一个特殊情况,就是假如尾部的元素在第一个怎么办?这时候tail-1就变为-1了。数组产生了越界。这时候报的错误是一堆看不懂的内存错误,让人摸不着头脑。
3.在入队的时候发生错误。逻辑错误。要牢记tail指向的是即将入队的空间。应该先入队,tail再++。
typedef struct { int* a; int head; int tail; int k; } MyCircularQueue; bool myCircularQueueIsEmpty(MyCircularQueue* obj) ; bool myCircularQueueIsFull(MyCircularQueue* obj) ; MyCircularQueue* myCircularQueueCreate(int k) { //给结构体指针变量开辟空间,否则为野指针。 MyCircularQueue* new =(MyCircularQueue*)malloc(sizeof(MyCircularQueue)); int* b = (int*)malloc(sizeof(int)*(k+1)); new->a = b; new->head = 0; new->tail = 0; new->k = k; return new; } bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) { assert(obj); if(myCircularQueueIsFull(obj)) { return false; } obj->a[obj->tail] = value; if(obj->tail == obj->k) { obj->tail = 0; } else { obj->tail++; } return true; } bool myCircularQueueDeQueue(MyCircularQueue* obj) { assert(obj); if(myCircularQueueIsEmpty(obj)) { return false; } if(obj->head == obj->k) { obj->head = 0; } else { obj->head++; } return true; } int myCircularQueueFront(MyCircularQueue* obj) { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } return obj->a[obj->head]; } int myCircularQueueRear(MyCircularQueue* obj) { assert(obj); if(myCircularQueueIsEmpty(obj)) { return -1; } if(obj->tail == 0) { return obj->a[obj->k]; } return obj->a[obj->tail-1]; } bool myCircularQueueIsEmpty(MyCircularQueue* obj) { assert(obj); return obj->head == obj->tail; } bool myCircularQueueIsFull(MyCircularQueue* obj) { assert(obj); if(obj->head==0 && obj->tail == obj->k) { return true; } else { return obj->head == obj->tail+1; } } void myCircularQueueFree(MyCircularQueue* obj) { assert(obj); free(obj->a); free(obj); }
用队列实现栈
用两个队列实现一个栈的基本功能。用C语言做,需要先创建两个队列。
题目描述
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
题目链接
思路分析
此题和C语言循环队列与用队列实现栈问题解析。差不多。
思路:
1.压栈就是谁不为空就往谁里面进行入队。
2.出栈就是先把不为空的一个队列里面的前k-1个元素入队到为空那个队列。然后再把不为空那个队列的元素pop掉。
代码实现
我遇到的bug
1.判断到底哪个队列是空队列,可以用假设法。假设其中一个为空,另一个不为空,然后再做调整。这样后续就方便了。
//假设后调整 Queue* emptyQ = &obj->q1; Queue* nonEmptyQ = &obj->q2; if(!QueueEmpty(&obj->q1)) { emptyQ = &obj->q2; nonEmptyQ = &obj->q1; }
2.移动前k-1个元素到另一个队列不能用遍历。遍历会麻烦,且每一次出队,头指针会自动移动。所以直接用算出队列的长度解决移动前k-1元素。
typedef int QDataType; typedef struct QueueNode { QDataType data; struct QueueNode* next; }QNode; typedef struct Queue { QNode* head; QNode* tail; //size_t size; }Queue; void QueueInit(Queue* pq); void QueueDestory(Queue* pq); void QueuePush(Queue* pq, QDataType x); void QueuePop(Queue* pq); bool QueueEmpty(Queue* pq); size_t QueueSize(Queue* pq); QDataType QueueFront(Queue* pq); QDataType QueueBack(Queue* pq); void QueueInit(Queue* pq) { assert(pq); pq->head = pq->tail = NULL; } void QueueDestory(Queue* pq) { assert(pq); QNode* cur = pq->head; while (cur) { QNode* next = cur->next; free(cur); cur = next; } pq->head = pq->tail = NULL; } void QueuePush(Queue* pq, QDataType x) { assert(pq); QNode* newnode = (QNode*)malloc(sizeof(QNode)); assert(newnode); newnode->data = x; newnode->next = NULL; if (pq->tail == NULL) { assert(pq->head == NULL); pq->head = pq->tail = newnode; } else { pq->tail->next = newnode; pq->tail = newnode; } } void QueuePop(Queue* pq) { assert(pq); assert(pq->head && pq->tail); if (pq->head->next == NULL) { free(pq->head); pq->head = pq->tail = NULL; } else { QNode* next = pq->head->next; free(pq->head); pq->head = next; } } bool QueueEmpty(Queue* pq) { assert(pq); return pq->head == NULL; } size_t QueueSize(Queue* pq) { assert(pq); QNode* cur = pq->head; size_t size = 0; while (cur) { size++; cur = cur->next; } return size; } QDataType QueueFront(Queue* pq) { assert(pq); assert(pq->head); return pq->head->data; } QDataType QueueBack(Queue* pq) { assert(pq); assert(pq->tail); return pq->tail->data; } //创建两个队列 typedef struct { Queue q1; Queue q2; } MyStack; //初始化两个队列 MyStack* myStackCreate() { MyStack* new = (MyStack*)malloc(sizeof(MyStack)); assert(new); QueueInit(&new->q1); QueueInit(&new->q2); return new; } //谁不为空就在谁里面入队 void myStackPush(MyStack* obj, int x) { assert(obj); if(!QueueEmpty(&obj->q2)) { QueuePush(&obj->q2, x); } else { QueuePush(&obj->q1, x); } } int myStackPop(MyStack* obj) { assert(obj); //假设后调整 Queue* emptyQ = &obj->q1; Queue* nonEmptyQ = &obj->q2; if(!QueueEmpty(&obj->q1)) { emptyQ = &obj->q2; nonEmptyQ = &obj->q1; } while(QueueSize(nonEmptyQ) > 1) { int front = QueueFront(nonEmptyQ); QueuePush(emptyQ, front); QueuePop(nonEmptyQ); } int top = QueueFront(nonEmptyQ); QueuePop(nonEmptyQ); return top; } int myStackTop(MyStack* obj) { assert(obj); int ret = 0; if(!QueueEmpty(&obj->q1)) { ret = QueueBack(&obj->q1); } else { ret = QueueBack(&obj->q2); } return ret; } bool myStackEmpty(MyStack* obj) { assert(obj); return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2); } void myStackFree(MyStack* obj) { assert(obj); QueueDestory(&obj->q1); QueueDestory(&obj->q2); free(obj); }
到此这篇关于C语言循环队列与用队列实现栈问题解析的文章就介绍到这了,更多相关C语言 循环队列内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!