[C language data structure 5]--implementation of stack

stack

1. What is a stack

A stack is a special kind of linear table, and we can think of a stack as a castrated version of a linear table. Its insertion and deletion operations can only be performed at the top of the stack . Therefore, it has the characteristics of last in first out (LIFO).

There are many examples of stacks in life, such as the ice candied haws that we usually eat, we need to eat the top before we can eat the bottom (although this is not the case, but I hope everyone will cooperate). Another example is when we take off our clothes, we have to take off the outer clothes before we can take off the inner clothes.

insert image description here

We can take a look at the picture below:

insert image description here

The middle part is a stack, and the top part of the stack is the top of the stack. The two most commonly used operations on the stack are push and pop operations. This group of operations are insertion and deletion operations, and they are only allowed on the top of the stack.

The push operation is to put new data above the top of the stack (logically), and then it becomes the new top of the stack. The pop operation is to delete the top element of the stack, and then let the element below the top of the stack be the top of the stack.

Second, the representation of the stack

We can implement stacks with sequential storage and chained storage structures. Let's take a look at how to represent a stack with two storage structures.

(1) Sequential storage structure

This is similar to the representation of the sequence table, but we need to set a pointer to the top of the stack.

#define MAXSIZE 20
typedef int ElemType;
typedef struct{
    
    
    ElemType data[MAXSIZE];		//静态数组,用于存放栈的元素
    int top;				   //栈顶指针
}SqStack;

A static array is defined in the structure to store the data in the stack, and a stack top pointer needs to be defined. The top of the stack pointer will always point to the index of the last element in the array (the top element of the stack).

(2) Chain storage structure

The stack implemented by the chained storage structure is similar to the singly linked list. The structure is as follows:

typedef struct SNode{
    
    
    ElemType data;			//数据域
    struct SNode *next;		//指针域
}*LinkedStack;

It will not be explained in detail here.

Because the operation of the stack is limited to the top of the stack, there is no problem of moving a large amount of data in the linear table, so we choose to use a sequential storage structure to implement each operation of the stack.

3. Implementation of the stack

(1) Initialization of the stack

Sequential storage can also be implemented in two ways, one is through static arrays, and the other is through malloc function to dynamically apply for memory.

For the former we only need to initialize the top of the stack, for the latter we also need to use malloc to allocate memory. There is little difference between the two, here we choose to use the former to achieve.

/**
*	用于初始化栈S
*/
int InitSqStack(SqStack *S){
    
    
    //将栈顶指向-1
    S->top = -1;
    return 1;
}

As we said earlier, the top of the stack pointer will always point to the subscript of the top element of the stack. The reason why it is not set to 0 here is because if the top of the stack is 0, it means another element in the stack. So at initialization we set it to -1.

(2) Push into the stack

The diagram of the stack is as follows:

insert image description here

For the push operation, we need to judge whether the stack is full. If it is full, return 0. If it is not full, we need to move the top pointer of the stack, and then push the element onto the stack.

int Push(SqStack *S, ElemType elem){
    
    
    //如果栈满了,则top == MAXSIZE-1,此时不进行入栈操作,返回0
    if(S->top == MAXSIZE-1){
    
    
        return 0;
    } 
    //移动栈顶指针
    S->top++;
    //将入栈元素放置在栈顶
    S->data[S->top] = elem;
    return 1;
}

We can also combine moving pointers and putting elements on top of the stack into one sentence:

//先进行++S->top移动指针,再将元素赋值到新top的位置
S->data[++S->top] = elem;

However, the readability is much worse. The author believes that in the case that the code will not affect the execution efficiency, it is better not to give up readability for the pursuit of code brevity.

(3) pop the stack

The pop operation and the push operation are the opposite. The diagram is as follows:

insert image description here

We first judge whether the stack is empty. If there is data in the stack, we first obtain the data, and then move the top pointer of the stack.

int Pop(SqStack *S, ElemType *elem){
    
    
    //如果栈为空,则top==-1,此时不进行出栈操作
    if(S->top == -1){
    
    
        return 0;
    }
    //获取栈顶元素
    *elem = S->data[S->top];
    //移动栈顶指针
    S->top--;
    return 1;
}

Because we want to get the data out of the stack, the elem passed in here is a pointer.

(4) Empty the stack

To clear the stack, we only need to complete the logical clearing, that is, assign the top of the stack to -1. The code is as follows:

void ClearStack(SqStack *S){
    
    
    //将栈顶指向-1
    S->top = -1;
}

(5) The stack is empty

When the stack is empty, we only need to judge whether top points to -1:

int EmptyStack(SqStack S){
    
    
  	//如果S.top==-1则返回1,如果S.top!=-1则返回0
    return S.top == -1 ? 1 : 0;
}

The ternary operator is used above, we can translate the above code into the following:

int EmptyStack(SqStack S){
    
    
    if(S.top == -1){
    
    
        return 1;
    }else{
    
    
        return 0;
    }
}

(6) Get the top element of the stack

In addition to popping the stack, we can also provide an operation to get the top element of the stack. The difference between it and pop is that it doesn't move the top pointer of the stack (it doesn't delete elements on the stack), so its operation is simpler than pop:

int GetTop(SqStack S, ElemType *elem){
    
    
    if(S.top == -1){
    
    
        return 0;
    }
    *elem = S.data[S.top];
    return 1;
}

In addition to the above operations, we can also implement many other operations. I won't go into details here.

Fourth, the application of stack

There are many applications for stacks, and you may feel that this structure is very redundant when you first get into the stack. But in fact, many functions of the operating system are based on the structure of the stack, such as numerical operations, program recursion, bracket matching, etc., which can be implemented using stacks. Let's implement a few simple examples below.

(1) Reverse order output

Now we ask the implementation to input a sequence and output in the reverse order of the sequence input.

Here we take advantage of the first-in-last-out feature of the stack. We push the data of the sequence into the stack in turn, and then pop it out of the stack in turn to achieve the reverse order output effect. The code is as follows:

void ReversePrint(int n){
    
    
    ElemType temp;
    SqStack S;
    InitSqStack(&S);
    //将输入元素依次入栈
    for(int i = 0; i < n; ++i){
    
    
        scanf("%d", &temp);
        Push(&S, temp);
    }
    //将元素出栈并输出
    for(int i = 0; i < n; ++i){
    
    
        Pop(&S, &temp);
        printf("%d\t", temp);
    }
}

(2) Base conversion

In the base conversion, the stack also plays the role of output in reverse order.

The principle of converting a decimal number N into a d-base number is as follows:
N 1 = N / ds 1 = N 1 % d N 2 = N 1 / ds 2 = N 2 % d . . . N n = N n − 1 / dsn = N n % d N_1 = N/d \\ s_1 = N_1\%d \\ N_2 = N_1/d \\ s_2 = N_2\%d \\...\\ N_n = N_{n-1 }/d \\ s_n = N_n \%dN1=N/ds1=N1%dN2=N1/ds2=N2%d. . .Nn=Nn1/dsn=Nn% d
where % is the modulo operation. We first divide the decimal number and then modulo the result of the division. The final d-base number of N is:
snsn − 1 . . . s 1 s_ns_{n-1}...s_1snsn1...s1
Let's do it in practice, where N is 1348 and d is 8:

N N/d N%d
1348 168 4
168 twenty one 0
twenty one 2 5
2 0 2

Finally, the octal number 1348 in decimal is 2504. Let's do it with code:

void conversion(){
    
    
    int n;
    //用于获取出栈元素
    ElemType e;
    SqStack S;
    InitSqStack(&S);
    scanf("%d", &n);
    while (n){
    
    
        //把式中s放入栈
        Push(&S, n % 8);
        n = n / 8;
    }
    while (!EmptyStack(S)){
    
    
  	    //逆序输出
        Pop(&S, &e);
        printf("%d\t", e);
    }
}

(3) Bracket matching

The general steps for bracket matching are as follows:

  1. Determine the content of the string in turn
  2. If it is a left parenthesis, it is directly pushed onto the stack
  3. If it is a closing bracket, match whether the top of the stack is the corresponding closing bracket
  4. If the match is successful, push the stack to continue the match, if the match fails, return 0
  5. After traversing the characters, if the stack is empty, the match is successful. If the stack is not empty, the match fails

We can simulate it with a few examples:

([)]
()()()
([][]{
    
    })
([][]{
    
    }))

There is no detailed analysis here, the following is the specific code:

int match(char str[], int len){
    
    
    ElemType elem;
    SqStack S;
    InitSqStack(&S);
    for(int i = 0; i < len; i++){
    
    
        //如果是左括号则直接入栈
        if(str[i] == '(' || str[i] == '[' || str[i] == '{'){
    
    
            Push(&S, str[i]);
        //如果是右括号,则进行匹配
        }else if(str[i] == ')'){
    
    
            //匹配成功则出栈
            if(GetTop(S, &elem) && elem == '('){
    
    
                Pop(&S, &elem);
            }else{
    
    
                return 0;
            }
        }else if(str[i] == ']'){
    
    
            if(GetTop(S, &elem) && elem == '['){
    
    
                Pop(&S, &elem);
            }else{
    
    
                return 0;
            }
        }else if(str[i] == '}'){
    
    
            if(GetTop(S, &elem) && elem == '{'){
    
    
                Pop(&S, &elem);
            }else{
    
    
                return 0;
            }
        }
    }
    if(EmptyStack(S)){
    
    
        return 1;
    }else{
    
    
        return 0;
    }
}

In addition, you can use the stack to implement some other algorithms.

Guess you like

Origin blog.csdn.net/ZackSock/article/details/118907908