美文网首页
Python数据结构-栈与深度优先搜索(Stack)

Python数据结构-栈与深度优先搜索(Stack)

作者: ShowMeCoding | 来源:发表于2022-01-14 17:40 被阅读0次

    栈的顺序存储实现

    class Stack:
        # 初始化空栈
        def __init__(self, size = 100):
            self.stack = []
            self.size = size
            self.top = -1     # 栈顶元素指向 -1
        # 判断栈是否为空
        def is_empty(self):
            return self.top == -1
        # 判断栈是否已满
        def is_full(self):
            return self.top + 1 = self.size
        # 入栈操作
        def push(self, value):
            if self.is_full():
                raise Exception('Stack is full!')
            else:
                self.stack.append(value)
                self.top += 1
        # 出栈操作
        def pop(self):
            if self.is_empty():
                raise Exception('Stack is empty!')
            else:
                self.stack.pop()
                self.top -= 1
        # 获取栈顶元素
        def peek(self):
            if self.empty():
                raise Exception('Stack is empty!')
            else:
                return self.stack[self.top]
    

    栈-先进后出

    class Test:
        def test(self):
            # create a stack
            stack = []
    
            # add element
            # time complexity: O(1)
            stack.append(1)
            stack.append(2)
            stack.append(3)
            print(stack)      # [1,2,3]
    
            # get the top of stack
            # time complexity: O(1)
            stack[-1]
    
            # remove the top of stack
            # time complexity: O(1)
            temp = stack.pop()
            print(temp)    # 3
    
            # get stack length
            # time complexity: O(1)
            len(stack)
    
            # stack is empty?
            # time complexity: O(1)
            len(stack) == 0
    
            # iterate stack
            # time complexity: O(n)
            while len(stack) > 0:
                temp = stack.pop()
                print(temp)         # 2 1
    
    if __name__ == '__main__':
        test = Test()
        test.test()
    

    3. 堆栈的应用

    堆栈是算法和程序中最常用的辅助结构,其的应用十分广泛。堆栈基本应用于两个方面:

    • 使用堆栈可以很方便的保存和取用信息,因此长被用作算法和程序中的辅助存储结构,临时保存信息,供后面操作中使用。
      例如:操作系统中的函数调用栈,浏览器中的前进、后退功能。
    • 堆栈的后进先出规则,可以保证特定的存取顺序。
      例如:翻转一组元素的顺序、铁路列车车辆调度。

    练习

    20. 有效的括号

    输入:s = "()[]{}"
    输出:true

    # 方法1:使用栈
    class Solution:
        def isValid(self, s: str) -> bool:
            stack = []
            # 首先判断字符串的长度是否为奇数
            if len(s)%2 == 1:
                return False
            # 用栈来完成判断
            for i in s:
                if i == '(' or i == '[' or i == '{':
                    stack.append(i)
                else:
                    # 先判断栈是否为空
                    if len(stack) == 0:
                        return False
                    top = stack.pop()
                    if i == ')' and top != '(':
                        return False
                    elif i == ']' and top != '[':
                        return False
                    elif i == '}' and top != '{':
                        return False
            # 最后看栈中元素是否为空
            if len(stack) == 0:
                return True
            else:
                return False
    
    227. 基本计算器 II

    整数除法仅保留整数部分。

    输入:s = " 3+5 / 2 "
    输出:5
    输入:s = " 3/2 "
    输出:1

    class Solution:
        def calculate(self, s: str) -> int:
            # 运算符
            cal = ['+', '-', '*', '/']
            # 辅助栈
            stack = []
            index = 0
            size = len(s)
            # 默认运算,因为除了空字符串,第一个字符串肯定为数字
            op = '+'
            # 字符串遍历
            while index < size:
                # 对于空格的灵活处理
                if s[index] == ' ':
                    index += 1
                    continue
                # 对于数字字符的处理
                if s[index].isdigit():
                    # 将字符串转化为数字
                    num = ord(s[index]) - ord('0')
                    # 对于连续多位数字字符串的处理
                    while index + 1 < size and s[index+1].isdigit():
                        # 需要注意及时更新索引
                        index += 1
                        num = 10*num + ord(s[index]) - ord('0')
                    # 得到数字之后和运算符一起压入栈中
                    if op == '+':
                        stack.append(num)
                    elif op == '-':
                        stack.append(0-num)
                    elif op == '*':
                        num_cal = stack.pop()
                        stack.append(num_cal*num)
                    # 整数除法仅保留整数部分
                    elif op == '/':
                        num_cal = stack.pop()
                        stack.append(int(num_cal/num))
                # 对于运算符的处理
                elif s[index] in cal:
                    op = s[index]
    
                index += 1
            return sum(stack)
    
    155. 最小栈

    设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
    push(x) —— 将元素 x 推入栈中。
    pop() —— 删除栈顶的元素。
    top() —— 获取栈顶元素。
    getMin() —— 检索栈中的最小元素。

    # 方法1:借用一个辅助栈,同时存储两个值:当前值,当前的最小值
    class MinStack:
        def __init__(self):
            """
            initialize your data structure here.
            """
            self.stack = []
        # 将元素压入栈
        def push(self, val: int) -> None:
            # 首先判断栈是否为空
            if len(self.stack) == 0:
                self.stack.append([val, val])
            else:
                self.stack.append([val, min(val, self.stack[-1][1])])
        # 元素弹出
        def pop(self) -> None:
           self.stack.pop()
        # 获取栈顶元素
        def top(self) -> int:
            return self.stack[-1][0]
        # 获取栈的最小值   
        def getMin(self) -> int:
            return self.stack[-1][1]
    
    739. 每日温度

    请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
    示例 1:
    输入: temperatures = [73,74,75,71,69,72,76,73]
    输出: [1,1,4,2,1,1,0,0]

    # 使用一个栈
    class Solution:
        def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
            stack = []
            length = len(temperatures)
            result = [0]*length
            count = 0
            for i in range(length):
                # 当栈不为空,且栈顶索引对应的温度小于当前温度
                while stack and temperatures[stack[-1]] < temperatures[i]:
                    # 索引出栈
                    index = stack.pop()
                    result[index] = i - index
                stack.append(i)
            return result
    
    150. 逆波兰表达式求值

    输入:tokens = ["2","1","+","3","*"]
    输出:9
    解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

    # 中缀表达式
    class Solution:
        def evalRPN(self, tokens: List[str]) -> int:
            stack = []
            cal = ['+', '-', '*', '/']
            for tok in tokens:
                if tok not in cal:
                    stack.append(int(tok))
                else:
                    a = stack.pop()
                    b = stack.pop()
                    if tok == '+':
                        ans = b + a
                    elif tok == '-':
                        ans = b - a
                    elif tok == '*':
                        ans = b * a
                    elif tok == '/':
                        ans = int(b / a)
                    stack.append(ans)
            return stack.pop()
    
    394. 字符串解码

    输入:s = "2[abc]3[cd]ef"
    输出:"abcabccdcdcdef"

    class Solution:
        def decodeString(self, s: str) -> str:
            stack_num = []
            stack_str = []
            s1 = ['a', 'b', 'c', 'd', 'e', 'f', 'g','h', 'i', 'j', 'k', 'l', 'm', 'n',
            'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
            num = 0
            res = ''
            for s_num in s:
                if s_num.isdigit():
                    num = num*10 + int(s_num)
                elif s_num in s1:
                    res += s_num
                elif s_num == '[':
                    stack_num.append(num)
                    stack_str.append(res)
                    # 及时初始化
                    num = 0
                    res = ''
                elif s_num ==']':
                    cur_num = stack_num.pop()
                    cur_res = stack_str.pop()
                    res = cur_res + cur_num * res
            return res
    
    946. 验证栈序列

    输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
    输出:true
    解释:我们可以按以下顺序执行:
    push(1), push(2), push(3), push(4), pop() -> 4,
    push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1

    # 对于pushed序列,每次进行push操作之后,有两种选择:继续push、弹出
    # 进行实际模拟,查看结果是否相符
    class Solution:
        def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
            stack = []
            i = 0
            for p in pushed:
                stack.append(p)
                while stack and stack[-1] == popped[i]:
                    stack.pop()
                    i += 1
                # stack.append(p)
            # 结束的判断:新建的栈是否都为空
            return not stack
    

    二、栈与深度优先搜索

    深度优先搜索算法(Depth First Search):英文缩写为 DFS。是一种用于遍历或搜索树或图的算法。该算法沿着树的深度遍历树的节点,会尽可能深的搜索树的分支。当节点 v 的所在边都己被探寻过,搜索将回溯到发现节点 v 的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。

    在深度优先遍历的过程中,我们需要将当前遍历节点 v 的相邻节点暂时存储起来,以便于在回退的时候可以继续访问它们。遍历到的节点顺序符合「后进先出」的特点,所以深度优先搜索可以通过「递归」或者「堆栈」来实现。

    2.1 基于递归实现的深度优先搜索

    def dfs_recrusive(graph, start, visited):
        ```
        graph: 存储无向图的字典变量
        start: 表示当前遍历边的开始节点
        visited: 标记访问节点的set集合变量
        ```
        visted = set()
        visited.add(start)
        # 遍历图
        for end in graph[start]:
            if end not in visited:
                # 深度优先遍历节点
                dfs_recursive(graph, end, visited)
    

    2.2 基于栈实现的广度优先搜索

    def dfs_stack(graph, start):
        # 定义起始节点,并把起始节点进行标记并放在栈中
        visited = set(start)
        stack = [start]
        # 递归结束的条件:栈为空
        while stack:
            node_u = stack.pop()
            for node_v in graph[node_u]:
                if node_v not in visited:
                    stack.append(node_v)
                    visted.appedn(node_v)
    
    200. 岛屿数量

    输入:grid = [
    ["1","1","1","1","0"],
    ["1","1","0","1","0"],
    ["1","1","0","0","0"],
    ["0","0","0","0","0"]
    ]
    输出:1

    class Solution:
        # 先定义深度优先搜索的算法
        def dfs(self, grid, i, j):
            # 计算行数和列数
            n = len(grid)
            m = len(grid[0])
            # 递归的终止条件
            if i < 0 or i >= n or j < 0 or j >= m or grid[i][j] == '0':
                return 0
            # 要及时将grid[i, j] = '1'的位置修改为'0',避免重复查找
            grid[i][j] = '0'
            # 对该点的上下左右进行检查
            self.dfs(grid, i + 1, j)
            self.dfs(grid, i, j + 1)
            self.dfs(grid, i - 1, j)
            self.dfs(grid, i, j - 1)
    
        def numIslands(self, grid: List[List[str]]) -> int:
            # 计数
            count = 0
            for i in range(len(grid)):
                for j in range(len(grid[0])):
                    if grid[i][j] == '1':
                        # count += 1
                        self.dfs(grid, i, j)
                        count += 1
            return count
    
    133. 克隆图

    给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
    图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。

    输入:adjList = [[2,4],[1,3],[2,4],[1,3]]
    输出:[[2,4],[1,3],[2,4],[1,3]]
    解释:
    图中有 4 个节点。
    节点 1 的值是 1,它有两个邻居:节点 2 和 4 。
    节点 2 的值是 2,它有两个邻居:节点 1 和 3 。
    节点 3 的值是 3,它有两个邻居:节点 2 和 4 。
    节点 4 的值是 4,它有两个邻居:节点 1 和 3 。

    """
    # Definition for a Node.
    class Node:
        def __init__(self, val = 0, neighbors = None):
            self.val = val
            self.neighbors = neighbors if neighbors is not None else []
    """
    # 深度优先算法
    class Solution:
        def dfs(self, node: 'Node', visited):
            # 递归的终止条件
            if node in visited:
                return visited[node]
            clone_node = Node(node.val, [])
            # 新建复制节点 clone_node,赋值为 node.val。
            visited[node] = clone_node
            for neighbor in node.neighbors:
                clone_node.neighbors.append(self.dfs(neighbor, visited))
            return clone_node
    
        def cloneGraph(self, node: 'Node') -> 'Node':
            if not node:
                return node
            # 使用字典变量 visited 存储访问过的节点,键值对为 原节点 : 新节点
            visited = dict()
            return self.dfs(node, visited)
    

    1)实战-括号匹配

    输入:(((()
    输出:False

    def parCheck(symbolString):
        s = Stack()
        balance = True
        index = 0
        while index < len(symbolString) and balance:
            if symbolString[index] == '(':
                s.push(symbolString[index])
            else:
                if s.isEmpty():
                    balance = False
                else:
                    s.pop()
            index += 1
        if balance and s.isEmpty():
            return True
        else:
            return False
    print(parCheck('()(((())))'))
    

    题目复杂化

    要求判别 {{{{[[[((()))]]]}}}}

    def parCheck(symbolString):
        s = Stack()
        balance = True
        index = 0
        while index < len(symbolString) and balance:
            symbol = symbolString[index]
            if symbol in '({[':
                s.push(symbolString[index])
            else:
                if s.isEmpty():
                    balance = False
                else:
                    # s.pop()
                    top = s.pop()
                    if not matches(top, symbol):
                        balance = False
            index += 1
        if balance and s.isEmpty():
            return True
        else:
            return False
    
    def matches(open, close):
        opens = "([{"
        closes = ")]}"
        return opens.index(open) == closes.index(close)
    
    print(parCheck('()(((([{(}]))))'))
    

    2)十进制转二进制

    def divideBy2(decNumber):
        stack = Stack()
    
        while decNumber > 0:
            rem = decNumber % 2   # 求余数
            stack.push(rem)
            decNumber //= 2       # 取整数
        
        binnumber = ''
        while not stack.isEmpty():
            binnumber += str(stack.pop())
        
        return binnumber
    print(divideBy2(5))
    

    进阶:十进制转16以下任意进制

    def divideBynum(decNumber, num):
        stack = Stack()
        digits = '0123456789ABCDEF'
    
        while decNumber > 0:
            rem = decNumber % num   # 求余数
            stack.push(rem)
            decNumber //= num       # 取整数
        
        binnumber = ''
        while not stack.isEmpty():
            binnumber += digits[stack.pop()]
        
        return binnumber
    print(divideBynum(5, 16))
    

    3)表达式转换

    中缀表达式 A + B; A + B * C; (A + B) * C
    前缀表达式 + AB ; + A * BC ; * +ABC
    后缀表达式 AB + ; A B C * +; AB + C*
    在中缀表达式中必须有的括号,在前缀和后缀表达式中消失了

    思路
    1 将中缀表达式转换为全括号的形式
    2 将所有的操作符移动到子表达式所在的左括号(前缀)或者右括号(后缀)处,再删除其它所有的括号

    def infixToPostfix(infixexpr):
        # 记录操作符的优先级
        prec = {}
        prec['*'] = 3
        prec['/'] = 3
        prec['+'] = 2
        prec['-'] = 2
        prec['('] = 1
    
        opStack = Stack()
        postfixList = []
        tokenList = infixexpr.split()    # 将表达式解析为列表
        # print(tokenList)
    
        for token in tokenList:
            if token in 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' or token in '0123456789':
                postfixList.append(token)
            elif token == '(':
                opStack.push(token)
            elif token == ')':
                topToken = opStack.pop()
                while topToken != '(':
                    postfixList.append(topToken)
                    topToken = opStack.pop()
            else:
                while (not opStack.isEmpty()) and (prec[opStack.peek()] >= prec[token]):
                    postfixList.append(opStack.pop())
                opStack.push(token)
    
        while not opStack.isEmpty():
            postfixList.append(opStack.pop())
        
        return " ".join(postfixList)       # 合成后缀表达式字符串
    
    a = '( A + B ) * C'            # 输入字符必须用空格分开,不然无法split
    print(infixToPostfix(a))
    

    计算后序表达式

    def compute1(s):
        operandStack = Stack()
        splitToken = s.split()
    
        for token in splitToken:
            if token in '0123456789':
                operandStack.push(int(token))
            else:
                a = operandStack.pop()
                b = operandStack.pop()
                operandStack.push(compute2(token, a, b))
        return operandStack.pop()
                
    def compute2(token, a,b):
        if token == '+':
            c = a+b
        elif token == '-':
            c = a-b
        elif token == '*':
            c = a * b
        else:
            c = a/b
        return c
    
    print(compute1('1 2 + 1 *'))     # 只能传入十以内数进行计算
    

    相关文章

      网友评论

          本文标题:Python数据结构-栈与深度优先搜索(Stack)

          本文链接:https://www.haomeiwen.com/subject/pcmevltx.html