[Data Revelation 02] Stack

The stack is a data structure based on the last-in-first-out (LIFO, Last In First Out) principle. It simulates a real-life stack, similar to a stack of plates or a pile of books.

The stack has two basic operations: push and pop.

  1. Push: Add a new element to the top of the stack. The new element becomes the top element of the current stack.
  2. Pop: Remove the top element from the top of the stack and return the value of that element.

In addition to these two basic operations, the stack can also support other common operations, such as:

  • Top: Gets the top element of the stack without removing it.
  • Empty test (isEmpty): Check whether the stack is empty.
  • Get size: Get the number of elements in the stack.

In fact, a stack can be implemented as an array or a linked list.

A stack implemented using an array is called an array-based stack. In a sequential stack, the end of the array is used as the top of the stack, and each push operation places an element at the end of the array, while a pop operation removes an element from the end of the array.

A stack implemented using a linked list is called a linked stack. In a chained stack, each node contains an element and a reference to the next node. The push operation inserts a new node at the head of the linked list, while the pop operation removes the node from the head of the linked list.

Stacks have a wide range of applications in computer science. For example, in programming, the stack is often used in the process of function calling. Whenever a function is called, its relevant information (such as parameters, local variables, etc.) will be pushed into the stack. When the function is executed, this information will be will be popped off the stack. This approach allows the program to track nested calls to functions and restore execution state correctly.

Stacks are also used to solve many other problems such as bracket matching, expression evaluation, depth-first search algorithms, backtracking algorithms, etc. Its simplicity and efficiency make the stack an important data structure.

Insert image description here
Insert image description here
Insert image description here

Abstract data description of the stack

Stack is an abstract data type (ADT) used to describe a data structure with last-in-first-out (LIFO, Last In First Out) characteristics. It defines the following operations:

  1. Initialize: Create an empty stack.
  2. Push: Adds a new element to the top of the stack.
  3. Pop: Removes the top element from the top of the stack and returns the value of that element.
  4. Top: Gets the top element of the stack without removing it.
  5. Empty test (IsEmpty): Check whether the stack is empty.
  6. Get size (Size): Get the number of elements in the stack.

These operations define the basic behavior and characteristics of the stack. Using these operations, various concrete stack implementations can be implemented, such as array- or linked-list based implementations.

Insert image description here
Insert image description here
Insert image description here
[Example] If three characters are pushed onto the stack in the order of ABC
• All permutations of ABC are possible
Is it a sequence that is popped from the stack?
• Is it possible to generate a sequence
like CAB?

During recursion, we maintain a stack and a pointer to the original sequence of characters. If the current top element of the stack is the same as the element pointed to by the pointer, it can be popped from the stack; otherwise, the element pointed to by the pointer needs to be pushed onto the stack. When all elements in the original character sequence have been pushed onto the stack, we can gradually pop the elements out of the stack to obtain a possible popping sequence.

Using the above method, we can get all possible pop sequences. If it contains a sequence starting with CAB, it means that CAB is a possible pop sequence. Otherwise, a sequence such as CAB cannot be generated.

Summary: Push the stack in sequence in the order of ABC. There are 6 possible pop sequences, namely ABC, ACB, BAC, BCA, CBA and CAB. Therefore, CAB is a possible pop sequence.

Sequential storage implementation of stack

Insert image description here

Insert image description here

#define MAXSIZE 100 // 定义栈的最大容量

typedef struct {
    
    
    ElementType data[MAXSIZE]; // 用数组存储栈元素
    int top; // 栈顶指针,指向当前栈顶元素的位置
} Stack;

// 初始化栈
void InitStack(Stack *S) {
    
    
    S->top = -1; // 初始化栈顶指针为-1,表示空栈
}

// 判断栈是否为空
int IsEmpty(Stack *S) {
    
    
    return (S->top == -1);
}

// 判断栈是否已满
int IsFull(Stack *S) {
    
    
    return (S->top == MAXSIZE - 1);
}

// 入栈操作
void Push(Stack *S, ElementType item) {
    
    
    if (IsFull(S)) {
    
    
        printf("Stack is full. Cannot push element %d.\n", item);
    } else {
    
    
        S->data[++(S->top)] = item;
    }
}

// 出栈操作
ElementType Pop(Stack *S) {
    
    
    if (IsEmpty(S)) {
    
    
        printf("Stack is empty. Cannot pop element.\n");
        return ERROR; // ERROR可以是一个预定义的错误值
    } else {
    
    
        return S->data[(S->top)--];
    }
}

// 获取栈顶元素
ElementType GetTop(Stack *S) {
    
    
    if (IsEmpty(S)) {
    
    
        printf("Stack is empty. No top element.\n");
        return ERROR;
    } else {
    
    
        return S->data[S->top];
    }
}

Insert image description here
Insert image description here

Chain storage implementation of stack

Insert image description here
Insert image description here

typedef struct StackNode {
    
    
    ElementType data; // 数据域
    struct StackNode *next; // 指针域,指向下一个节点
} StackNode;

typedef struct {
    
    
    StackNode *top; // 栈顶指针,指向当前栈顶元素
} LinkedStack;

// 初始化栈
void InitStack(LinkedStack *S) {
    
    
    S->top = NULL; // 初始化栈顶指针为空,表示空栈
}

// 判断栈是否为空
int IsEmpty(LinkedStack *S) {
    
    
    return (S->top == NULL);
}

// 入栈操作
void Push(LinkedStack *S, ElementType item) {
    
    
    StackNode *newNode = (StackNode *)malloc(sizeof(StackNode)); // 创建新节点
    newNode->data = item; // 设置新节点的数据域为要入栈的元素
    newNode->next = S->top; // 将新节点插入到栈顶
    S->top = newNode; // 更新栈顶指针
}

// 出栈操作
ElementType Pop(LinkedStack *S) {
    
    
    if (IsEmpty(S)) {
    
    
        printf("Stack is empty. Cannot pop element.\n");
        return ERROR; // ERROR可以是一个预定义的错误值
    } else {
    
    
        StackNode *temp = S->top; // 保存当前栈顶节点
        ElementType item = temp->data; // 获取栈顶元素的值
        S->top = temp->next; // 更新栈顶指针
        free(temp); // 释放原栈顶节点
        return item;
    }
}

// 获取栈顶元素
ElementType GetTop(LinkedStack *S) {
    
    
    if (IsEmpty(S)) {
    
    
        printf("Stack is empty. No top element.\n");
        return ERROR;
    } else {
    
    
        return S->top->data;
    }
}

Stack App: Expression Evaluation

When it comes to expression evaluation, we can consider the use of stacks. Here is a more complex example that demonstrates how to use the stack to evaluate infix expressions.

Suppose the expression we want to solve is an infix expression: (3 + 4) * 5 - 6 / 2

  1. Create an empty stack and operator precedence dictionary.
  2. Iterate over each element in the infix expression from left to right.
  3. If the current element is a number, it is converted to an integer and pushed directly onto the stack.
  4. If the current element is an operator, do the following:
    • If the stack is empty or the top element of the stack is the left bracket "(", the current operator is pushed onto the stack.
    • If the stack is not empty and the priority of the current operator is greater than the priority of the operator on top of the stack, the current operator is pushed onto the stack.
    • If the stack is not empty and the priority of the current operator is less than or equal to the priority of the operator on the top of the stack, the operator on the top of the stack is popped and calculated, and the calculation result is pushed onto the stack. Repeat this step until the condition is met, then push the current operator onto the stack.
    • If the current element is the right bracket ")", the operator on the top of the stack is popped and calculated until it encounters the left bracket "(". The left bracket "(" is popped from the stack, but no calculation is performed.
  5. After traversing the infix expression, the remaining operators in the stack are popped and calculated.
  6. There is only one element left on the stack, which is the result of the expression.

The following are the specific steps to evaluate the infix expression (3 + 4) * 5 - 6 / 2:

  1. Create an empty stack and operator priority dictionary (addition and subtraction have priority 1, multiplication and division have priority 2).
  2. Traverse to "(" and push it onto the stack.
  3. Traverse to 3, convert it to an integer and push it onto the stack.
  4. Traverse to "+", the top of the stack is "(", push "+" onto the stack.
  5. Traverse to 4, convert it to an integer and push it onto the stack.
  6. Traverse to ")", pop up the operator "+" on the top of the stack and perform calculations to get 3+4=7, and push the calculation result 7 onto the stack.
  7. traverses to "", the top element of the stack is 7, the priority is greater than "", and "*" is pushed onto the stack.
  8. Traverse to 5, convert it to an integer and push it onto the stack.
  9. Traverse to "-", the top element of the stack is "", the priority is less than or equal to "-", pop the top operator of the stack""And perform calculation, get 7*5=35, and push the calculation result 35 onto the stack.
  10. Traverse to 6, convert it to an integer and push it onto the stack.
  11. Traverse to "/", the top element of the stack is 6, the priority is less than or equal to "/", pop up the top operator "/" and perform calculation, get 6/2=3, and push the calculation result 3 onto the stack.
  12. After traversing the infix expression, there is only one element left on the stack, 35-3, which is the calculation result of the expression.

Therefore, the infix expression (3 + 4) * 5 - 6 / 2 has the value 32.

How to convert an infix expression into a postfix expression

A common way to convert an infix expression into a postfix expression is to use a stack.

Here are the steps of the conversion process:

  1. Create an empty stack and an empty list to store postfix expressions.
  2. Iterate over each element in the infix expression from left to right.
  3. If the current element is a number or letter, it is added directly to the end of the suffix expression list.
  4. If the current element is a left bracket "(", push it onto the stack.
  5. If the current element is a right bracket ")", the elements in the stack are popped and added to the suffix expression list until the left bracket "(" is encountered. The left bracket is then popped from the stack but not added. into the postfix expression list.
  6. If the current element is an operator (such as "+", "-", "*", "/", etc.), the following operations are performed:
    • If the stack is empty, push the current operator onto the stack.
    • If the stack is not empty and the top element of the stack is the left bracket "(", the current operator is pushed onto the stack.
    • If the stack is not empty and the priority of the current operator is less than or equal to the priority of the operator on the top of the stack, pop the operator on the top of the stack and add it to the postfix expression list. Repeat this step until the condition is met, and then remove the current operator from the stack. Push onto the stack.
    • If the stack is not empty and the priority of the current operator is greater than the priority of the operator on top of the stack, the current operator is pushed onto the stack.
  7. After traversing the infix expression, pop the remaining operators from the stack and add them to the postfix expression list.
  8. The postfix expression list is the converted postfix expression.

Here is an example:

Infix expression: 2 + 3 * 4

Conversion process:

  1. Traverse to 2 and add directly to the suffix expression list.
  2. Traverse to +, the stack is empty, push it onto the stack.
  3. Traverse to 3 and add directly to the suffix expression list.
  4. Traverse to *, the stack is not empty, the top of the stack is +, push * onto the stack.
  5. Traverse to 4 and add directly to the suffix expression list.
  6. After traversing the infix expression, pop the remaining operators + and * in the stack and add them to the postfix expression list.

Converted postfix expression: 2 3 4 * +

Therefore, the infix expression 2 + 3 * 4 can be converted into the postfix expression 2 3 4 * +.

Insert image description here
Insert image description here
Insert image description here

Insert image description here

Guess you like

Origin blog.csdn.net/shaozheng0503/article/details/134894246