“莫听穿林打叶声,何妨吟啸且徐行”
这里是目录
循环队列
循环队列: 循环队列是一种线性数据结构,其操作表现基于 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)。
题目链接
思路分析
此题和用栈实现队列往期博客是个兄弟题。差不多。
思路:
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); }