Stack
Structure and concept of stack
Stack: a special linear table in which elements are allowed to be inserted and deleted only at the fixed end. The section for data insertion and deletion is called the top of the stack, and the other end is called the bottom of the stack. The elements in the stack follow the principle of first in and last out.
Stack pressing: the stack insertion operation is called stack entering / stack entering / stack pressing, and the input data is at the top of the stack.
Out of stack: the stack deletion operation is called out of stack, and the out data is also at the top of the stack.


Implementation of stack
Stack implementation can generally use array or linked list, but the structure of array should be better. Because the cost of inserting data on the tail of the array is relatively small.


In fact, both data structures can implement linked lists, but why choose arrays
Because of the characteristics of the stack, if a linked list is used, the top of the stack, that is, the position of the entry and exit elements, is at the end of the linked list. Each time you enter and exit the stack, you have to traverse the whole linked list from the first node.
Stack implementation:
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #include<assert.h> typedef int Datatype; typedef struct Stack { Datatype* a; int top; int Capacity; }Stack; void StackInit(Stack* p); //Initialization stack void StackPush(Stack* p, Datatype x); //Stack operation void StackPop(Stack* p); //Out of stack operation Datatype StackTop(Stack* p); //Returns the value of the top element of the stack int StackEmpty(Stack* p); //Check whether the stack is empty void StackDestroy(Stack* p); //Destroy stack void StackPrint(Stack* p); //Print stack elements
The above time stack structure and specific basic operations
The following is the implementation of these specific operations
#define _CRT_SECURE_NO_WARNINGS 1 #include"stack.h" void StackInit(Stack* p) { assert(p); p->a = (Datatype *)malloc(4*sizeof(Datatype)); p->top = 0; p->Capacity = 4; } void StackPush(Stack* p, Datatype x) { assert(p); if (p->top == p->Capacity) //Check whether to increase capacity { p->a = (Datatype *)realloc(p->a,2 * p->Capacity*sizeof(Datatype)); p->Capacity = p->Capacity * 2; } p->a[p->top] = x; p->top++; } void StackPop(Stack* p) { assert(p); if(p->top) p->top = p -> top - 1; } void StackPrint(Stack* p) { assert(p); for (int i = 0; i < p->top; i++) { printf(" %d ", p->a[i]); } printf("\n"); } Datatype StackTop(Stack* p) { return p->a[p->top - 1]; } int StackEmpty(Stack* p) { return p->top == 0; } void StackDestroy(Stack* p) { free(p->a); p->a = NULL; p->top = 0; p->Capacity = 0; }
Some applications of stack
Bracket matching problem
//The stack implementation and corresponding functions should be referenced above. In order to see and judge the functions more clearly, I will not copy them above bool isValid(char * s){ Stack ps; StackInit(&ps); while(*s) { if(*s=='(' || *s=='{' || *s=='[') { StackPush(&ps,*s); s++; } else { if(StackEmpty(&ps)) return false; char top=StackTop(&ps); StackPop(&ps); if((top=='[' && *s!=']')||(top=='{' && *s!='}')||(top=='(' && *s!=')')) return false; s++; } } if(StackEmpty(&ps)) return true; return false; }
The idea of this question is:
- Loop through the string s. if * s is any of '(', '{' and '[', put the character on the stack
- If * s is not the above characters, that is') ','} 'and'] ', it matches the elements at the top of the stack. If it matches, it will take the elements at the top of the stack out of the stack. If it does not match, it will directly return false to end the judgment. return true after the loop is completed


However, some special situations should be noted:
- When the string is "(", judge whether the stack is empty before return true at the end. If the stack is not empty, there are unmatched characters in the stack, so return false
- When the string is ")", we should judge whether the stack is empty before taking the top element of the stack in the second part. If it is empty, it means that there is no left bracket matching the right bracket in the stack, and false is returned
Implement queue with stack
leetcode - queue implementation with stack
The main difficulty of using queue to realize stack is in two operations: out of stack and return to the top element of stack
But the idea of the two functions is the same
Take the out of stack function myQueuePop function as an example
Here, in order to implement the queue, we need to create two stacks to reverse each other to take out the first element of the queue. The specific operations are as follows:
-
First, put the top element of s1 stack into s2 stack, and then take the top element out of stack. Repeat until the s1 stack is empty.
-
At this time, the elements of the whole stack are reversed. The team head element is now at the top of the stack. The following operations are slightly different in myQueuePop and myQueuePeek
- myQueuePop To put s2 The value at the top of the stack is out of the stack - myQueuePeek Don't put s2 The value of the top of the stack
-
Finally, first put the top element of s2 stack into s1 stack, and then take the top element out of stack. Repeat until the s2 stack is empty
code:
#include<stdio.h> #include<stdlib.h> #include<assert.h> typedef int Datatype; typedef struct Stack { Datatype* a; int top; int Capacity; }Stack; void StackInit(Stack* p) { assert(p); p->a = (Datatype*)malloc(4 * sizeof(Datatype)); p->top = 0; p->Capacity = 4; } void StackPush(Stack* p, Datatype x) { assert(p); if (p->top == p->Capacity) //Check whether to increase capacity { p->a = (Datatype*)realloc(p->a, 2 * p->Capacity * sizeof(Datatype)); p->Capacity = p->Capacity * 2; } p->a[p->top] = x; p->top++; } void StackPop(Stack* p) { assert(p); if (p->top) p->top = p->top - 1; } void StackPrint(Stack* p) { assert(p); for (int i = 0; i < p->top; i++) { printf(" %d ", p->a[i]); } printf("\n"); } Datatype StackTop(Stack* p) { return p->a[p->top - 1]; } int StackEmpty(Stack* p) { return p->top == 0; } void StackDestroy(Stack* p) { free(p->a); p->a = NULL; p->top = 0; p->Capacity = 0; } //The above are the basic operations of the stack, and the following is the implementation of the queue typedef struct { Stack s1; Stack s2; } MyQueue; /** Initialize your data structure here. */ MyQueue* myQueueCreate() { MyQueue* q=(MyQueue*)malloc(sizeof(MyQueue)); StackInit(&(q->s1)); StackInit(&(q->s2)); return q; } /** Push element x to the back of queue. */ void myQueuePush(MyQueue* obj, int x) { int a = 0; StackPush(&(obj->s1), x); } /** Removes the element from in front of queue and returns that element. */ int myQueuePop(MyQueue* obj) { while (StackEmpty(&(obj->s1))==0) { StackPush(&(obj->s2), StackTop(&(obj->s1))); StackPop(&(obj->s1)); } Datatype a = StackTop(&(obj->s2)); StackPop(&(obj->s2)); while (StackEmpty(&(obj->s2))==0) { StackPush(&(obj->s1), StackTop(&(obj->s2))); StackPop(&(obj->s2)); } return a; } /** Get the front element. */ int myQueuePeek(MyQueue* obj) { while (StackEmpty(&(obj->s1)) == 0) { StackPush(&(obj->s2), StackTop(&(obj->s1))); StackPop(&(obj->s1)); } Datatype a = StackTop(&(obj->s2)); while (StackEmpty(&(obj->s2)) == 0) { StackPush(&(obj->s1), StackTop(&(obj->s2))); StackPop(&(obj->s2)); } return a; } /** Returns whether the queue is empty. */ bool myQueueEmpty(MyQueue* obj) { return StackEmpty(&(obj->s1)); } void myQueueFree(MyQueue* obj) { StackDestroy(&(obj->s1)); StackDestroy(&(obj->s2)); obj = NULL; }
queue
Concept and structure of queue
Queue: a special linear table that allows inserting data only in one section and deleting data at the other end. The queue has a first in first out FIFO (first in first out) into the queue, the end of the queue that performs the insertion operation is called the tail of the queue, and the section that performs the deletion is called the head of the queue
Implementation of queue
The queue can also be implemented by using the structure of array and linked list. It is better to use the structure of linked list, because if the structure of array is used, the efficiency will be relatively low
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<assert.h> #include<stdlib.h> typedef int DataType; typedef struct BinaryTree { DataType x; struct BinaryTree* _left; struct BinaryTree* _right; }BT; typedef BT* Datatype; typedef struct ListNode { Datatype x; struct ListNode* next; }ListNode; typedef struct Queue { ListNode* head; ListNode* tail; }Queue; void QueueInit(Queue* q); //Initialize queue void QueuePush(Queue* q, Datatype x); // Join the team void QueuePop(Queue* q); // Out of the team Datatype QueueFront(Queue* q); //Output queue header element Datatype QueueBack(Queue* q); // Elements at the end of the output queue int QueueSize(Queue* q); //Size of the output queue int QueueEmpty(Queue* q); //Determine whether the queue is empty void QueueDestroy(Queue* q); //Queue destruction
The above time stack structure and specific basic operations
The following is the implementation of these specific operations
#define _CRT_SECURE_NO_WARNINGS 1 #include"Queue.h" void QueueInit(Queue* q) { ListNode* First = (ListNode*)malloc(sizeof(ListNode)); q->head = q->tail = First; q->tail->next = NULL; } void QueuePush(Queue* q, Datatype x) { assert(q); q->tail->x = x; ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode)); q->tail->next = NewNode; q->tail = NewNode; q->tail->next = NULL; } void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); if (!QueueEmpty(q)) { ListNode* temp = q->head->next; free(q->head); q->head = temp; } } Datatype QueueFront(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->head->x; } Datatype QueueBack(Queue* q) { assert(q); assert(!QueueEmpty(q)); ListNode* cur = q->head; while (cur->next != q->tail) { cur = cur->next; } return cur->x; } int QueueSize(Queue* q) { if (QueueEmpty(q)) { return 0; } ListNode* cur = q->head; int k = 0; while (cur != q->tail) { cur = cur->next; k++; } return k; } int QueueEmpty(Queue* q) { return q->head == q->tail; } void QueueDestroy(Queue* q) { ListNode* cur = q->head; while (cur != q->tail) { ListNode* temp = cur->next; free(cur); cur = temp; } free(cur); cur = NULL; q->head = NULL; q->tail = NULL; }
Application of queue
Implement stack with queue
Implementation idea: two queues are used to realize the function of stack. The implementation of stack queue is different, mainly in the function myStackPop. How to get the elements of the end of the queue is the difficulty of this problem:
The above is my thinking:
- First, ensure that q1 is always the queue for storing data, and q2 is an empty queue for importing data
- Copy n-1 elements in q1 into q2 respectively and take them out of the queue, leaving the end of the queue element (top of the stack) and take them out of the queue (in other words, copy the elements with q1 divided by the end of the queue into q2 and delete them)
- Swap q1 with q2
#include<stdio.h> #include<assert.h> #include<stdlib.h> typedef int Datatype; typedef struct listNode { Datatype x; struct listNode* next; }ListNode; typedef struct { ListNode* head; ListNode* tail; }Queue; void QueueInit(Queue* q) { ListNode* First = (ListNode*)malloc(sizeof(ListNode)); First->next = NULL; q->head = q->tail = First; q->tail->next = NULL; } int QueueEmpty(Queue* q) { return q->head == q->tail; } void QueuePush(Queue* q, Datatype x) { assert(q); q->tail->x = x; ListNode* NewNode = (ListNode*)malloc(sizeof(ListNode)); q->tail->next = NewNode; q->tail = NewNode; q->tail->next = NULL; } void QueuePop(Queue* q) { assert(q); assert(!QueueEmpty(q)); if (!QueueEmpty(q)) { ListNode* temp = q->head->next; free(q->head); q->head = temp; } } Datatype QueueFront(Queue* q) { assert(q); assert(!QueueEmpty(q)); return q->head->x; } Datatype QueueBack(Queue* q) { assert(q); assert(!QueueEmpty(q)); ListNode* cur = q->head; while (cur->next != q->tail) { cur = cur->next; } return cur->x; } int QueueSize(Queue* q) { if (QueueEmpty(q)) { return 0; } ListNode* cur = q->head; int k = 0; while (cur != q->tail) { cur = cur->next; k++; } return k; } void QueueDestroy(Queue* q) { ListNode* cur = q->head; while (cur != q->tail) { ListNode* temp = cur->next; free(cur); cur = temp; } free(cur); cur = NULL; q->head = NULL; q->tail = NULL; } void check(Queue* q1, Queue* q2) { if (QueueEmpty(q1)) { Queue temp = *q1; *q1 = *q2; *q2 = temp; } } typedef struct { Queue q1; Queue q2; } MyStack; /** Initialize your data structure here. */ MyStack* myStackCreate() { MyStack* p = (MyStack*)malloc(sizeof(MyStack)); QueueInit(&(p->q1)); QueueInit(&(p->q2)); return p; } /** Push element x onto stack. */ void myStackPush(MyStack* obj, int x) { check(&(obj->q1), &(obj->q2)); QueuePush(&(obj->q1), x); } /** Removes the element on top of the stack and returns that element. */ int myStackPop(MyStack* obj) { check(&(obj->q1), &(obj->q2)); int b = 0; while (!QueueEmpty(&(obj->q1))) { if (QueueSize(&(obj->q1)) == 1) { b = QueueFront(&(obj->q1)); QueuePop(&(obj->q1)); continue; } int a = QueueFront(&(obj->q1)); QueuePop(&(obj->q1)); QueuePush(&(obj->q2), a); } return b; } /** Get the top element. */ int myStackTop(MyStack* obj) { check(&(obj->q1), &(obj->q2)); return QueueBack(&(obj->q1)); } /** Returns whether the stack is empty. */ bool myStackEmpty(MyStack* obj) { check(&(obj->q1), &(obj->q2)); return QueueEmpty(&(obj->q1)); } void myStackFree(MyStack* obj) { QueueDestroy(&(obj->q1)); QueueDestroy(&(obj->q2)); free(obj); }
Circular queue
Logical structure of circular queue
The circular queue consists of a Front offset, a Rear offset and a sequence table. Logically, it is a ring structure. However, in the physical storage structure, it is actually a sequence table, which makes the Front and Rear offsets cycle through the array under certain conditions.
Logical structure:
Actual physical structure:
Structural design
How to judge whether a queue is empty or full
If we think about it carefully, we will find that both the empty and full teams coincide. If we use Rear==Front, we can't judge the empty and full teams
So we solve this problem by adding an extra space (K+1)
When Rear==Front, we think the circular queue is empty
Since Rear always points to the next space of the tail element, when an empty node is set to make the queue full, Rear and Front do not coincide. However, the condition Rear+1==Front is satisfied, but the judgment condition here should be written as ((obj - > Rear+1)% (obj - > capacity)) in the process of writing ==Obj - > Front because Rear+1 may cross the array, it should be returned to the starting point of the array to reach the loop.
typedef struct { int *a; int Front; int Rear; int capacity; } MyCircularQueue; MyCircularQueue* myCircularQueueCreate(int k) { MyCircularQueue*q=(MyCircularQueue*)malloc(sizeof(MyCircularQueue)); q->a=(int *)malloc((k+1)*sizeof(int)); q->Front=0; q->Rear=0; q->capacity=k+1; return q; } bool myCircularQueueIsFull(MyCircularQueue* obj) { return ((obj->Rear+1)%(obj->capacity))==obj->Front; } bool myCircularQueueIsEmpty(MyCircularQueue* obj) { return ((obj->Front)%(obj->capacity))==obj->Rear; } bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) { if(myCircularQueueIsFull(obj)) //Check if the queue is full return false; obj->a[obj->Rear]=value; obj->Rear++; obj->Rear%=obj->capacity; return true; } bool myCircularQueueDeQueue(MyCircularQueue* obj) { if(myCircularQueueIsEmpty(obj)) return false; obj->Front++; obj->Front%=obj->capacity; 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->Rear - 1+obj->capacity)%obj->capacity]; } void myCircularQueueFree(MyCircularQueue* obj) { free(obj->a); free(obj); obj=NULL; }
Easy to make mistakes!
Let's share the problems I encountered when writing this oj:
- First, it is not judged whether the circular linked list is empty in the myCircularQueueFront and myCircularQueueRear functions
- The second problem I found after looking for a long time is that myCircularQueueRear returns the value of the end of the queue. Since Rear always points to the next node, the end element is a [(obj - > Rear - 1]. However, here, it is necessary to judge whether Rear-1 meets the requirements of circular array. If it is not checked, it will always report heap - overflow