preface
This paper focuses on the knowledge of data structure stack and queue. Since this paper also designs multiple dynamic memory development functions, before learning this paper, we must firmly grasp the relevant knowledge of dynamic memory development, so that we can get twice the result with half the effort.
Tip: the following is the main content of this article. The following cases can be used for reference
1, Representation and implementation of stack
1.1 concept and structure of stack
Stack: a special linear table (logically data is placed together), which allows insertion and deletion operations only at a fixed end. One end for data insertion and deletion is called the top of the stack, and the other end is called the bottom of the stack. The data elements in the stack follow the principle of last in first out.
Stack pressing: the stack insertion operation is called stack entering / line pressing / line entering, and the incoming data is at the top of the stack.
Stack out: stack deletion is called stack out. The output data is also at the top of the stack.
(picture from bit employment class)
First in and then out is similar to loading a pistol clip. The bullets you put in first will be at the bottom of the clip, and the last bullets you put in are at the top of the clip. When you open a pistol, you first shoot the bullets at the top of the clip.
Let's first define a stack type:
typedef int STDataType;//If you need other types of data in the future, you can directly modify the type of int typedef struct Stack { STDataType*a;//Stack bottom pointer int top;//Stack top label int capacity;//capacity }ST;
This paper introduces the realization of stack with sequential list (array). Therefore, the so-called stack pressing is just the tail inserting and tail deleting of sequential list. If readers want to realize it with linked list, the method is not unique.
1.2 stack initialization
About stack initialization: we take the stack with a capacity of 4 as an example. At the beginning, because there is no data in the stack, we mark it with top=0. It should be noted that the pointer at the bottom of the stack passed can not be null. When opening up a space, if a null pointer is returned in case of unsuccessful development, it should also be discarded.
#include<assert.h> #Include < stdlib. H > / / malloc function header file void StackInit(ST*ps)//Stack initialization { assert(ps);//Prevent the pointer passed from being null ps->a = (STDataType)malloc(sizeof(STDataType) * 4);//malloc opens up a space to be managed by the STDataType type //malloc, and the realloc and free functions that appear later, see the author's dynamic memory management article for details if (ps->a == NULL) { printf("realloc fail\n"); exit(-1);//Termination procedure } ps->capacity=4; //Here to open up a stack of four data sizes as an example, you can also write other numbers //If there is not enough memory when pressing the stack, you can expand the capacity in the stack pressing function mentioned later ps->top = 0;//When there is no value in the stack at the beginning, it is marked with top=0, and each subsequent top is put in++ //For the detailed usage of top, please see the section 1.3 stack pressing }
1.3 stack pressing (insert a data at the top of the stack)
As shown in the figure above, we have now opened up a space. Both a and top are at the top of the stack. We put a data 1 into it
After 1 enters the stack, 1 is the top element of the stack. If I want to continue entering the stack, I need to put a data in the top position to make the newly placed data become a new top element, and so on. One element is entered each time, top + +. Top does not represent the position of the top element, but the next position of the top element.
#Include < assert. H > / / assert function header file #Include < stdlib. H > / / realloc function header file void StackPush(ST*ps, STDataType x)//Insert data at the top of the stack (stack) { assert(ps); if (ps->top == ps->capacity) { STDataType*tmp = realloc(ps->a, ps->capacity * 2 * sizeof(STDataType)); //realloc is to continue to open up a space in the future on the original space. See the author's dynamic memory article for details //Capacity expansion is generally 2 times if (tmp == NULL)//If the expansion fails (for example, there is not enough memory for you to open up space), a null pointer will be returned { printf("realloc fail\n"); exit(-1);//Termination procedure } else { ps->a = tmp; ps->capacity *= 2; } } ps->a[ps->top] = x;//A is a pointer, a[x]==*(a+x) ps->top++; }
1.4 out of stack (delete a data at the top of the stack)
Stacking is very simple. Let's take the following figure as an example:
In the figure, we have stored four data 1, 2, 3 and 4 in the stack. What should we do now, that is, delete 4? top – directly
There must be some friends who have questions here. Why do you top - it's out of the stack, and you 4 haven't deleted it? The explanation is as follows: we said in the stack pressing part of 1.3 that "top does not represent the position of the top element, but the next position of the top element". Now I have top 4 here, which shows that 3 is the real top element, and 4 is no longer under the jurisdiction of the stack.
void StackPop(ST*ps)//Delete data at the top of stack (out of stack) { assert(ps); assert(ps->top > 0);//When the stack is empty, call Pop to directly abort the program and report an error ps->top--; }
PS - > top > 0 is like this: suppose you didn't have an element in the original stack
You can access the unknown domain from the top – this is a very serious problem, so we use assert here to prevent any element in the stack from being marked to the unknown domain. (again, top does not indicate the position of the top element, but the next position of the top element)
1.5 stack top elements
STDataType StackTop(ST*ps)//Fetch stack top data { assert(ps); assert(ps->top > 0);//If the stack is empty, adjust StackTop and directly abort the program with an error return ps->a[ps->top - 1];//Top is the next position of the stack top element, and top-1 is the stack top element //a[m]==*(a+m) }
The idea here is almost the same as that in 1.4. You should also prevent nothing in the stack, and then return to the top element normally, because top is the next position of the top element, top-1 is the top element, and then you can write it normally
A [PS - > Top - 1] you can also write * (a + (PS - > Top - 1)). Readers can participate in the author's previous pointer articles, which will not be repeated here.
1.6 stack top elements
int StackSize(ST*ps)//Number of stack data { assert(ps); return ps->top; }
This is even simpler. Just return the value of top directly. Why? Let's just look at two diagrams
Figure 1:
Figure 2:
Figure 1 shows that there is no element before pressing the stack, top=0. Figure 2 shows that after pressing the stack, top + +, top=1. Similarly, by analogy, for every element we add, top + +, and for every element we subtract, top –. The number of elements is always equal to the top value, so our function here can directly return the top value.
1.7 judge whether the stack is empty
int StackEmpty(ST*ps)//Determine whether the stack is empty { assert(ps); return ps->top == 0; }
From 1.6, our top value is the same as the number of elements in the stack, so we directly return PS - > Top = = 0; If PS - > Top = = 0 is true, the expression value is 1, otherwise it is 0.
2, Representation and implementation of queue
2.1 concept and structure of queue
Queue: a special linear table that only allows data operations to be inserted at one end and deleted at the other end. The queue has the nature of first in first out
Enter queue: the end of the queue at which the insertion operation is performed is called the end of the queue
Out of queue: the end of the deletion operation is called the queue head
(picture from bit employment class)
It's similar to the way you go through the artificial channel. If you're in front, you go out first
2.2 implementation of queue
Since the queue head goes out and the queue tail goes in, which is very similar to the header deletion and tail insertion of the single linked list, we introduce the implementation of the queue with the single linked list, as shown in the following figure. There are three single linked lists of nodes:
For example, now, I want the team leader to delete the first node of the single linked list node (header deletion)
Or the tail of the team into one, that is, the tail insertion of the single linked list
The code is as follows (example):
typedef int QDataType; typedef struct QueueNode { struct QueueNode*next; QDataType data; }QNode;//Here is the same as the definition of single linked list. If necessary, you can see the previous single linked list article of the author typedef struct Queue//Define a structure to store the head node address and tail node address to facilitate subsequent head deletion and tail insertion { QNode*head; QNode*tail; }Queue;
2. Queue initialization
The code is as follows (example):
void QueueInit(Queue*pq)Queue initialization,pq Is a structure pointer { assert(pq);//Determine whether pq is a null pointer pq->head = NULL; pq->tail = NULL; }
3. Join the team (insert a data at the end of the team)
void QueuePush(Queue*pq, QDataType x)//Join the team (join at the end of the team) { assert(pq); QNode*newnode = (QNode*)malloc(sizeof(QNode));//Open up a space for newnode if (newnode == NULL)//It is possible that the remaining memory is not enough to open up space. If malloc fails to open up, it will return a null pointer { printf("Failed to open up space\n"); exit(-1);//Exit program } newnode->data = x; newnode->next = NULL; if (pq->tail == NULL)//Originally, there was no data in the queue, and the header and tail pointers pointed to NULL { pq->head = pq->tail = newnode; } else { pq->tail->next = newnode; pq->tail = newnode; } }
The following if code is explained as follows:
if (pq->tail == NULL) { pq->head = pq->tail = newnode; } else { pq->tail->next = newnode; pq->tail = newnode; }
Originally, there was no data in the queue, and the header and tail pointers pointed to NULL
When you put a data into the queue, the head and tail pointers point to the new data (the head and tail pointers still point to one)
If you want to add another node, you must first connect the two nodes, that is, PQ - > tail - > next = newnode; (before connection, the tail also points to the first node). After connection, the tail pointer points to the second node
3. Out of the team (delete a data at the team head)
void QueuePop(Queue*pq)//Out of line (team leader out) { assert(pq); assert(pq->head);//To get out of the team, the team should also have data to get out //The original queue has only 1 data if (pq->head->next == NULL) { free(pq->head); pq->head = pq->tail = NULL; } //The original queue has multiple data else { QNode*next = pq->head->next; free(pq->head); pq->head = next; } }
There are two situations to discuss about leaving the team:
1. There is only one data in the original queue
When there is only one data, the head and tail pointers point to node 1, and their next is a null pointer, so we judge by this condition. And because we want to get out of the queue (delete a data at the head of the queue), because there is only one node, that is, delete this node. We use the free function to release the space pointed to by the head. After the release, the space has been returned to the memory. At this time, your head and tail pointers cannot point to the space. We assign values with null pointers.
2. There are multiple data in the original queue
Now we need to get out of the queue (delete a data at the head of the queue), that is, delete node 1. We first create a variable next to find the location of node 2, and then free the space of node 1
After the node space is free, assign the next (pointing to node 2) to the head and make the head pointer point to node 2.
4. Get team header data
QDataType QueueFront(Queue*pq)//Fetch header data { assert(pq); assert(pq->head); return pq->head->data; }
5. Get the tail data
QDataType QueueBack(Queue*pq)//Fetch tail data { assert(pq); assert(pq->head); return pq->tail->data; }
6. Take the tail data
int QueueSize(Queue*pq)//Number of data in the team { assert(pq); int size = 0; QNode*cur = pq->head; while (cur)//cur!= Loop through NULL, which occurs after traversing the tail { size++; cur = cur->next; } return size; }
7. Judge whether the queue is empty
bool QueueEmpty(Queue*pq) { assert(pq); return pq->head == NULL; }
This is generally used as an auxiliary function. bool is the data type used to judge whether it is true or false. If the expression: PQ - > head = = null is true, it returns true, otherwise it returns false
8. Destroy queue
void QueueDestory(Queue*pq)//Queue destruction { assert(pq); QNode*cur = pq->head; while (cur)//cur!= Loop through NULL, which occurs after traversing the tail { QNode*next = cur->next; free(cur);//free is to release the space pointed to, and the pointer is still in cur = next; } pq->head = pq->tail = NULL; }
As shown in the figure above, we create a variable cur and assign a value with the head pointer. Then find the second node and mark it with cur - > next. After marking, we release cur (the pointer is still in the first node space). After releasing, cur starts to traverse the second node, that is, the space we marked with next... The rest, and so on. cur!=NULL is used to cycle. NULL appears after traversing the tail. When the cycle does not explain that it has traversed the node pointed to by the tail pointer, and the head and tail nodes are no longer needed, we use the NULL pointer to assign values.
summary
This paper introduces the relevant principles of stack and queue and various interface functions. There are many contents and a large number of knowledge points. I hope readers can learn patiently. I believe you will gain something. I wish readers a happy study!