❤️栈和队列实现
文章目录
💟栈
🚗 1、什么是栈
栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。**进行数据插入和删除操作的一端称为栈顶(Top),另一端称为栈底(Bottom)。**栈中的数据元素遵守后进先出LIFO(Last In First Out)原则。即后入栈的元素先出栈。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也叫栈顶。
🚐 2、栈源码查看
🚘 3、栈的基本操作
void StackInit(ST* ps); //初始化空栈 void StackDestroy(ST* ps); //销毁栈 void StackPush(ST* ps, STDataType x); //入栈,将x入栈到栈顶 void StackPop(ST* ps); //出栈 STDataType StackTop(ST* ps); //返回栈顶元素 bool StackEmpty(ST* ps); //判断栈非空 int StackSize(ST* ps); //返回栈的大小
🚔4、栈实现(动态顺序表实现)
//初始化空栈 void StackInit(ST* st) { assert(st); st->stack = NULL; st->capacity = 0; st->size = 0; } //将栈置为空栈 void StackDestory(ST* st) { assert(st); if (st->stack == NULL) { return; } free(st->stack); st->stack = NULL; st->size = 0; st->capacity = 0; } //入栈 void StackPush(ST* st, StackDataType x) { assert(st); //先检查容量 if (st->size == st->capacity) { int newcapacity = st->capacity == 0 ? 4 : st->capacity * 2; StackDataType* newstack = (StackDataType*)realloc(st->stack, sizeof(StackDataType) * newcapacity); if (newstack == NULL) { perror("StackPush realloc fail"); exit(-1); } st->capacity = newcapacity; st->stack = newstack; } st->stack[st->size] = x; st->size++; } //出栈 void StackPop(ST* st) { assert(st); assert(!StackEmpty(st));//空为true,非空为假报错 st->size--; } //返回栈顶元素 StackDataType StackTop(ST* st) { assert(st); assert(!StackEmpty(st)); return st->stack[st->size - 1]; } //返回栈的长度 size_t StackSize(ST* st) { assert(st); return st->size; } //判断栈是否为空(返回true为空) bool StackEmpty(ST* st) { assert(st); return st->size == 0; }
💗 队列
🚓 1、什么是队列
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列具有先进先出FIFO(First In First Out)的特性。
入队列:进行插入操作的一端称为队尾(rear)
出队列:进行删除操作的一端称为队头(front)
顺序队列:建立顺序队列结构必须为其静态分配或动态申请一片连续的存储空间,并设置两个指针进行管理。一个是队头指针(front),它指向队头元素,另一个是队尾指针rear,它指向下一个入队元素的存储位置
循环队列:在实际使用队列时,为了使队列空间能够重复使用,往往对队列的使用方法稍加改进:无论插入或者删除,一旦rear指针增1或front指针增1时超过了所分配的队列空间,就让它指向这片连续空间的起始位置。
🚃 2、队列源码查看
🚌 3、队列的基本操作
//初始化空队列 QueueInit(&queue); //将队列置为空队列 QueueDestory(&queue); //入队列 QueuePush(&queue,x); //出队列 QueuePop(&queue); //返回队列长度 QueueSize(&queue); //判断是否为空 QueueEmpty(&queue); //返回头部元素 QueueFront(&queue); //返回尾部元素 QueueRear(&queue);
🚌 4、队列实现(链表实现)
typedef int QueueDataType; typedef struct QueueNode { struct QueueNode* next; QueueDataType data; }QNode; //因为队列特殊的结构,将头指针和尾指针创建一个结构体 typedef struct Queue { QNode* head; QNode* tail; }QE; //初始化空队列 void QueueInit(QE* queue) { assert(queue); queue->head = queue->tail = NULL; //初始化到这 } //将队列置为空队列 void QueueDestory(QE* queue) { assert(queue); QNode* cur = queue->head; while (cur != NULL) { QNode* del = cur; cur = cur->next; free(del); } queue->head = queue->tail = NULL; } //入队列 void QueuePush(QE* queue, QueueDataType x) { assert(queue); //增加新节点 QNode* newnode = (QNode*)malloc(sizeof(QNode)); if (newnode == NULL) { perror("QueuePush malloc fail"); exit(-1); } newnode->data = x; newnode->next = NULL; //插入:考虑当队列为空的时候 if (queue->tail == NULL && queue->head == NULL) { queue->head = queue->tail = newnode; } else { queue->tail->next = newnode; queue->tail = newnode; } } //出队列 void QueuePop(QE* queue) { assert(queue); assert(!QueueEmpty(queue)); // 非空为假就报警告 //考虑只有一个节点的情况 if (queue->head == queue->tail) { free(queue->head); queue->head = queue->tail = NULL; } else { QNode* del = queue->head; queue->head = queue->head->next; free(del); del = NULL; } } //返回队列长度 size_t QueueSize(QE* queue) { assert(queue); QNode* cur = queue->head; size_t size = 0; while (cur != NULL) { ++size; cur = cur->next; } return size; } //判断是否为空(返回真就为空) bool QueueEmpty(QE* queue) { assert(queue); return queue->tail == NULL; } //返回头部元素 QueueDataType QueueFront(QE* queue) { assert(queue); assert(!QueueEmpty(queue)); return queue->head->data; } //返回尾部元素 QueueDataType QueueRear(QE* queue) { assert(queue); assert(!QueueEmpty(queue)); return queue->tail->data; }
💕 栈和队列相关的OJ题
💕 1、设计循环队列(定长数组实现)
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
题目分析
:
- 什么是循环队列
在实际使用队列,为了使队列空间能够重复使用,往往对队列的使用方法稍加改进:即无论插入或者删除,一旦rear指针增1或front指针增1时超过了所分配的队列空间,就让它指向这片连续空间的起始位置。
这实际上是把队列空间想象成一个环形空间,环形空间中的存储单元循环使用,用这种方法管理的队列也就称为循环队列。这也就意味着循环队列所能存储的数据大小是固定的。
如图:
- 缺陷分析
front为队头指针,指向队头元素,rear为队尾指针,指向下一个入队元素的存储位置。
那当循环队列为空时,有front =rear。当循环队列满时,也有front = rear。
即循环队列的缺陷无法判断队列为空或为满这两种情况!
- 解决方案
1、在实现循环队列时,多加一个参数size用来记录循环队列的长度。size = 0时,队列为空,size = maxsize时,队列为满。
2、在实现循环队列时,多增加一个额外的节点。例队列可存储四个数据,就申请五个数据的空间,当front = rear时,队列为空,当front = rear+1时,队列为满。
3、当front、rear增一就超出所分配的内存空间时,可以让front%maxsize,rear%maxsize
- 代码实现
typedef struct { int* a; int front;//头指针 int back; //尾指针 int N;//记录循环链表实际开辟空间的个数 } MyCircularQueue; //判断非空 bool myCircularQueueIsEmpty(MyCircularQueue* obj) { return obj->front == obj->back; } //判断是否满 bool myCircularQueueIsFull(MyCircularQueue* obj) { return (obj->back+1) % obj->N == obj->front; } //创建空循环队列 MyCircularQueue* myCircularQueueCreate(int k) { MyCircularQueue* myqueue = (MyCircularQueue*)malloc(sizeof(MyCircularQueue)); myqueue->a = (int*)malloc(sizeof(int)*(k+1)); myqueue->front = 0; myqueue->back = 0; myqueue->N = k+1; return myqueue; } //入队列 bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) { if( myCircularQueueIsFull(obj)) { return false; } obj->a[obj->back] = value; obj->back++; //当其超过所分配的空间内存时回到头 obj->back %= obj->N; return true; } //出队列 bool myCircularQueueDeQueue(MyCircularQueue* obj) { if(myCircularQueueIsEmpty(obj)) { return false; } obj->front++; //当其超过所分配的空间内存时回到头 obj->front %= obj->N; return true; } //返回队头元素 int myCircularQueueFront(MyCircularQueue* obj) { if(myCircularQueueIsEmpty(obj)) { return -1; } return obj->a[obj->front]; } //返回队尾元素 int myCircularQueueRear(MyCircularQueue* obj) { if(myCircularQueueIsEmpty(obj)) { return -1; } return obj->a[(obj->back-1+obj->N)%obj->N]; } //将队列置为空队列,同时释放空间 void myCircularQueueFree(MyCircularQueue* obj) { free(obj->a); free(obj); }