python小记-栈

栈(Stack)是一种常见的线性数据结构,它遵循“后进先出”(Last-In-First-Out,LIFO)的原则。这意味着最后入栈的元素将首先被弹出,而最先入栈的元素将最后被弹出。栈主要支持两种操作:入栈(Push)和出栈(Pop)。

栈的主要特点包括:

  1. 入栈(Push):将元素添加到栈顶的操作,也称为压栈。

  2. 出栈(Pop):从栈顶弹出元素的操作,也称为弹栈。

  3. 栈顶(Top):栈中最后一个元素。

  4. 栈底(Bottom):栈中第一个元素。

  5. 空栈(Empty Stack):栈中没有任何元素。

栈的应用场景非常广泛,一些常见的应用包括:

  1. 函数调用栈:在函数调用过程中,每次调用一个新函数,都会将函数的局部变量和返回地址等信息保存在栈中。当函数执行完成后,会从栈中弹出这些信息,回到上一个函数。

  2. 表达式求值:在编程中,栈可以用于计算表达式的值。例如,可以使用栈来求解后缀表达式。

  3. 括号匹配:栈可以用于检查括号是否匹配。遍历表达式中的字符,将左括号入栈,遇到右括号时,弹出栈顶元素并判断是否与当前右括号匹配。

  4. 浏览器的前进和后退功能:浏览器的历史记录可以用栈来实现,每次浏览一个新页面,将其添加到栈顶;点击后退按钮时,弹出栈顶页面。

  5. 迷宫求解:在迷宫中搜索路径时,可以使用栈来记录当前的位置和已走过的路径。

栈可以通过数组或链表来实现。在编程中,通常会使用系统自带的栈数据结构或通过数组或链表手动实现一个栈。栈的操作非常高效,入栈和出栈的时间复杂度均为 O(1)。由于其简单易用和高效性,栈在算法和程序设计中得到广泛应用。

栈在遍历中的应用

  1. 深度优先遍历(DFS):

    • 应用场景:深度优先遍历在图的搜索和遍历问题中非常常见,例如查找连通分量、判断图是否有环、计算最短路径等。它在解决类似迷宫、棋盘等问题时也有广泛应用。
    • 实现原理:深度优先遍历通过递归或栈的方式实现。使用栈时,从起始节点开始,不断将当前节点的未访问邻居入栈,直到栈为空。在遍历过程中,对每个节点进行标记以避免重复访问。
  2. 非递归的二叉树遍历:

    • 应用场景:非递归的二叉树遍历可以用于解决二叉树相关的问题,例如查找二叉搜索树中的某个节点、判断二叉树是否对称、将二叉树展开为链表等。
    • 实现原理:非递归的二叉树遍历使用栈来模拟递归过程。在中序遍历中,先将根节点入栈,然后不断将当前节点的左子节点入栈,直到最左边的叶子节点。然后出栈一个节点,访问该节点,再将当前节点更新为右子节点,重复上述步骤,直到栈为空且当前节点为None。前序遍历和后序遍历的实现类似,只是入栈和出栈的顺序不同。

这两种遍历方式都是图和树相关问题中的常用算法,它们的应用广泛且灵活。在实际应用中,根据具体问题的特点选择合适的遍历方式可以帮助我们高效地解决问题。例如,在处理迷宫问题时,深度优先遍历可以用于搜索路径;在处理二叉搜索树时,非递归的中序遍历可以得到有序的元素序列;在处理棋盘问题时,深度优先遍历可以帮助我们找到所有可能的解等。

树DFS

假设有如下二叉树:

      1
     / \
    2   3
   / \
  4   5

可以使用以下代码来进行深度优先遍历:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def dfs(root):
    if not root:
        return

    print(root.val)  # 在遍历过程中处理当前节点

    dfs(root.left)   # 递归遍历左子树
    dfs(root.right)  # 递归遍历右子树

# 创建二叉树
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

# 进行深度优先遍历
dfs(root)

输出结果为:

1
2
4
5
3

以上是对给定二叉树的深度优先遍历结果。首先访问根节点 1,然后递归遍历左子树,先访问节点 2,再访问节点 4 和 5,然后回溯到节点 2,继续访问节点 3。这样完成了深度优先遍历。

非递归的二叉树遍历与递归的方法有何区别

非递归的二叉树遍历与递归的方法有以下主要区别:

  1. 使用栈或队列:非递归的二叉树遍历使用栈或队列来辅助实现遍历过程,而递归的方法则是通过系统栈实现递归调用。

  2. 显式控制遍历顺序:在非递归的遍历中,我们需要显式地控制节点的访问顺序,将左子节点或右子节点先入栈或队列,从而实现前序、中序、后序遍历。而递归的方法会自动按照函数调用栈的顺序实现遍历。

  3. 额外空间:非递归的遍历需要使用额外的栈或队列来存储节点,而递归的方法不需要额外的数据结构,只使用系统栈。

  4. 代码复杂度:通常情况下,非递归的遍历代码相对复杂,需要处理边界条件、循环控制等,而递归的方法则相对简单,直观。

递归的二叉树遍历是一种直观、简单的实现方式,但可能存在栈溢出的风险,尤其是在二叉树很深的情况下。而非递归的遍历则可以避免栈溢出的问题,并且可以优化空间复杂度。但是非递归的实现可能稍微复杂一些,需要注意边界条件和循环控制。选择使用哪种方法取决于具体的场景和需求。

图DFS

深度优先遍历(DFS)图的实现代码可以使用递归或栈来完成。下面是使用递归的实现方式:

from collections import defaultdict

class Graph:
    def __init__(self):
        self.graph = defaultdict(list)

    def addEdge(self, u, v):
        self.graph[u].append(v)

    def DFS(self, v, visited):
        visited[v] = True
        print(v, end=' ')

        for i in self.graph[v]:
            if not visited[i]:
                self.DFS(i, visited)

    def DFS_main(self):
        V = len(self.graph)
        visited = [False] * V

        for i in range(V):
            if not visited[i]:
                self.DFS(i, visited)

# 示例
g = Graph()
g.addEdge(0, 1)
g.addEdge(0, 2)
g.addEdge(1, 2)
g.addEdge(2, 0)
g.addEdge(2, 3)
g.addEdge(3, 3)

print("深度优先遍历结果:")
g.DFS_main()

运行上述代码,将会输出深度优先遍历的结果:

深度优先遍历结果:
0 1 2 3

注意:上述代码是针对无向图的深度优先遍历实现。对于有向图,需要根据具体情况调整邻接节点的遍历顺序。同时,需要注意图中可能存在孤立节点(没有任何边连接的节点),因此在遍历时需要处理这种情况。

猜你喜欢

转载自blog.csdn.net/qq_40140808/article/details/131911086