The beauty of data structure and algorithm | stack

  • Stack structure: last in first out, advanced last out

  • A stack is a linear list with "restricted operations"

  • When a data set only involves inserting and deleting data at one end, and satisfies the characteristics of last-in-first-out and first-in-last-out, then we should prefer the "stack" data structure

Implementation of the stack

  • Implementation using arrays: sequential stack
class ArrayStack:
    """使用数组实现一个顺序栈"""

    def __init__(self):
        '''
        初始化顺序栈

        参数:
            无

        返回值:
            无
        '''
        
        self.items = []

    def push(self, item):
        '''
        入栈操作

        参数:
            item (任意类型): 待入栈的值

        返回值:
            无
        '''
        self.items.append(item)

    def pop(self):
        '''
        出栈操作

        参数:
            无

        返回值:
            待出栈的值
        '''
        if not self.is_empty():
            return self.items.pop()

    def is_empty(self):
        '''
        检查栈是否为空

        参数:
            无

        返回值:
            bool: 如果栈为空则返回True,否则返回False
        '''
        return len(self.items) == 0

  • Implementation using linked list: linked stack
class Node:
    '''维护链表节点'''
    def __init__(self, value):
        '''
        初始化链表节点

        参数:
            value (任意类型): 节点的值

        返回值:
            无
        '''
        # 节点的值
        self.value = value
        # 指向下一个节点的指针
        self.next = None

class Stack:
    '''进行出栈或入栈操作'''
    def __init__(self):
        '''
        初始化栈

        参数:
            无

        返回值:
            无
        '''
        # 栈顶节点
        self.top = None

    def push(self, value):
        '''
        入栈操作

        参数:
            value (任意类型): 要入栈的值

        返回值:
            无
        '''
        if self.top is None:
            self.top = Node(value)
        else:
            # 创建新节点
            new_node = Node(value)
            # 将新节点指向当前的栈顶节点
            new_node.next = self.top
            # 更新栈顶节点为新节点
            self.top = new_node

    def pop(self):
        '''
        出栈操作

        参数:
            无

        返回值:
            出栈的节点的值
        '''
        if self.top is None:
            return None
        else:
            # 弹出栈顶节点
            popped_node = self.top
            # 更新栈顶节点为下一个节点
            self.top = self.top.next
            # 将弹出的节点从链表中断开
            popped_node.next = None
            # 返回弹出节点的值
            return popped_node.value

stack application

function call stack

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

def main():
    '''
    主函数,用于执行程序的主要逻辑

    参数:
        无

    返回值:
        int: 表示程序执行结果的返回值
    '''
    a = 1
    ret = 0
    res = 0

    # 调用add函数,将返回值赋给ret变量
    ret = add(3, 5)

    # 计算a和ret的和,将结果赋给res变量
    res = a + ret

    # 打印res的值
    print(res)

    return 0


def add(x, y):
    '''
    将两个数字相加

    参数:
        x (int): 第一个数字
        y (int): 第二个数字

    返回值:
        int: 两个数字的和
    '''
    sum = 0

    # 计算x和y的和,将结果赋给sum变量
    sum = x + y

    return sum


if __name__ == '__main__':
    # 调用main函数
    main()

img

Can function calls be implemented using other data structures?

  • While other data structures can also be used to hold temporary variables, their implementation may not be as efficient as a stack .
  • For example, using a linked list to save context information requires traversing the linked list when inserting and deleting elements, and the time complexity is O(n), while the time complexity of using a stack is O(1). Therefore, the stack is the most commonly used implementation of the function call stack.

expression evaluation

The compiler implements it through two stacks. One holds the operand stack and the other holds the operator stack.

  1. We traverse the expression from left to right, and when we encounter a number, we push it directly onto the operand stack;
  2. When an operator is encountered, it is compared with the top element of the operator stack. If it has a higher priority than the top element of the operator stack, push the current operator onto the stack;
  3. If the priority of the element at the top of the operator stack is lower or the same, take the top operator from the operator stack, take 2 operands from the top of the operand stack, perform calculations, and then push the calculated results into Operand stack, continue comparison.

img

Checks for matching parentheses

Suppose there are only three kinds of parentheses in the expression:

  • Parentheses ()

  • Square brackets[]

  • curly braces{}

They can be nested arbitrarily. For example, {[] ()[{}]}or [{()}([])]and so on are legal formats, while {[}()]or [({)]is an illegal format.

So how to check if the parentheses are legal?

  1. We use a stack to store unmatched opening brackets, and scan the string from left to right.

  2. When a left parenthesis is scanned, it is pushed onto the stack; when a right parenthesis is scanned, a left parenthesis is taken from the top of the stack. If it can match, such (as )match with, [match ]with, { match }with, continue to scan the remaining strings. If during the scanning process, an unmatched closing parenthesis is encountered, or there is no data in the stack, it indicates an illegal format.

  3. After all the brackets are scanned, if the stack is empty, it means that the string is in a legal format; otherwise, it means that there are unmatched left brackets, which is an illegal format.

Implement the browser's forward and backward functions

  • Use two stacks, X and Y.
  • We push the pages browsed for the first time into stack X in turn, and when the back button is clicked, they are popped from stack X in turn, and the popped data are put into stack Y in turn. When we click the forward button, we sequentially take data from stack Y and put them into stack X.
  • When there is no data in the stack X, it means that there is no page to browse back. When there is no data in stack Y, it means that there is no page to browse by clicking the forward button.

What is the difference between stack and stack in JVM?

The "stack" in the JVM is similar to the "stack" in the data structure, which is a last-in-first-out (LIFO) data structure. But they are implemented and used differently.

In the JVM, each thread has a private stack, which is used to store local variables and operand stacks during method calls. When a method is called, a new stack frame is created on the stack to store information such as the method's local variables and operand stack. When the method returns, the stack frame corresponding to the method is destroyed. Therefore, the "stack" in the JVM is mainly used for method calls and returns, and for storing thread-private data.

In computer science, a stack usually refers to a data structure that is a last-in-first-out (LIFO) data structure that can only be inserted and deleted at the top of the stack. The stack is usually used to store temporary variables and return addresses when a function is called, and to implement algorithms such as recursion .

Leetcode: Stack exercises

topic:

  • 20
  • 155
  • 232
  • 844
  • 224
  • 682
  • 496

  1. Whenever a function is called, a new stack frame (Stack Frame) will be created to save the function's local variables, parameters, return address and other information↩︎

Guess you like

Origin blog.csdn.net/YuvalNoah/article/details/131157726