04_In-depth understanding of the stack

1. How to implement a "stack"

The stack can be implemented with an array or a linked list. The stack implemented with an array is called a sequential stack, and the stack implemented with a linked list is called a chain stack.
Sequence stack code based on array

// 基于数组实现的顺序栈
public class ArrayStack {
  private String[] items;  // 数组
  private int count;       // 栈中元素个数
  private int n;           //栈的大小

  // 初始化数组,申请一个大小为n的数组空间
  public ArrayStack(int n) {
    this.items = new String[n];
    this.n = n;
    this.count = 0;
  }

  // 入栈操作
  public boolean push(String item) {
    // 数组空间不够了,直接返回false,入栈失败。
    if (count == n) return false;
    // 将item放到下标为count的位置,并且count加一
    items[count] = item;
    ++count;
    return true;
  }
  
  // 出栈操作
  public String pop() {
    // 栈为空,则直接返回null
    if (count == 0) return null;
    // 返回下标为count-1的数组元素,并且栈中元素个数count减一
    String tmp = items[count-1];
    --count;
    return tmp;
  }
}

2. Sequential stack that supports dynamic expansion

As the chain stores so much next stack pointer, so we used the array to achieve
the expansion mechanism is similar to an array expansion, when the array is not enough space to re-apply for a bigger memory, the last of the original data in the array copy
complexity analysis:
if it is a The stack is undoubtedly O(1).
If it is into the stack, the best is O(1), and the worst is O(n). On average, using the amortization method, it is O(1) (it can be found that the average amortization method is general Are equal to the best case)

3. The application of stack in function call

The operating system allocates an independent memory space for each thread, this memory is organized into a "stack" structure, used to store temporary variables when function calls. Every time a function is entered, the temporary variable will be pushed into the stack as a stack frame. When the called function is executed and returns, the stack frame corresponding to this function will be popped out of the stack.

int main() {
   int a = 1;
   int ret = 0;
   int res = 0;
   ret = add(3, 5);
   res = a + ret;
   printf("%d", res);
   reuturn 0;
}

int add(int x, int y) {
   int sum = 0;
   sum = x + y;
   return sum;
}

The calling process is shown in the figure:
Insert picture description here

4. The application of stack in expression evaluation

The compiler is implemented through two stacks. One of them holds the stack of operands and the other holds the stack of operators. We traverse the expression from left to right. When we encounter a number, we directly push it into the operand stack; when we encounter an operator, we compare it with the top element of the operator stack.
If the priority is higher than that of the top element of the operator stack, the current operator is pushed onto the stack; if the priority is lower or the same than the priority of the top element of the operator stack, the top operator is taken from the operator stack and from the operand stack Take 2 operands from the top of the stack, then perform calculations, and then push the calculated results into the operand stack to continue the comparison.

Example: 3+5*8-6
Insert picture description here

5. The application of stack in bracket matching

Suppose the expression contains only three kinds of parentheses, parentheses (), square brackets [] and curly brackets {}, and they can be nested arbitrarily. For example, {[] ()[{}]} or [{()}([])] are all legal formats, while {[}()] or [({)] are illegal formats. Then I will give you an expression string containing three kinds of parentheses. How can I check whether it is legal?
You can also use the stack here. We use the stack to store the unmatched left parenthesis and scan the string from left to right. When the left bracket is scanned, it is pushed onto the stack; when the right bracket is scanned, a left bracket is taken from the top of the stack. If it can match, for example, "(" matches ")", "[" matches "]", and "{" matches "}", then continue to scan the remaining strings. If during scanning, you encounter unmatched right parenthesis, or there is no data in the stack, it means that it is an illegal format.
When all the brackets are scanned, if the stack is empty, the string is in a legal format; otherwise, it indicates that there is an unmatched left bracket, which is an illegal format.

Thinking after class

  1. Why do function calls use "stacks" to store temporary variables? Isn't it possible to use other data structures?
    In fact, we don't have to use the stack to store temporary variables, but if this function call conforms to the characteristics of last in, first out, it is the most logical choice to use the data structure of the stack to implement it.
    From the calling function to the called function, what does the data change? Is the scope. So basically, as long as you can ensure that every new function is entered, it is a new scope. To achieve this, using a stack is very convenient. When entering the called function, allocate a section of stack space to the variable of this function, and reset the top of the stack at the end of the function, just returning to the scope of the calling function.

  2. There is a "stack" concept in JVM memory management. Stack memory is used to store local variables and method calls, and heap memory is used to store objects in Java. Is the "stack" in the JVM the same as the "stack" we are talking about here? If not, why is it called a "stack"?
    The stack in the memory and the data structure stack are not a concept. It can be said that the stack in the memory is a real physical area, and the stack in the data structure is an abstract data storage structure.
    The memory space is logically divided into three parts: code area, static data area and dynamic data area. The dynamic data area is divided into stack area and heap area.
    Code area: store the binary code of the method body. High-level scheduling (job scheduling), intermediate scheduling (memory scheduling), and low-level scheduling (process scheduling) control the switching of code execution in the code area.
    Static data area: store global variables, static variables, and constants, including final modified constants and String constants. The system automatically allocates and reclaims.
    Stack area: store the formal parameters, local variables, and return values ​​of the running method. Automatically allocated and recycled by the system.
    Heap area: The reference or address of a new object is stored in the stack area, pointing to the real data of the object stored in the heap area.

Related topics

leetcode20,155,232,844,224,682,496.

Reference Course: "The Beauty of Data Structure and Algorithm" by Mr. Wang Zheng , a geek time

Guess you like

Origin blog.csdn.net/Yungang_Young/article/details/112797182