树-列表递归的伟大问题

2024 年 8 月 28 日 | 阅读 6 分钟

引言

“大树链表递归”概念涉及通过递归算法将二叉树结构和链表相结合,创建一种兼具树和链表特性的多功能数据结构。

该范式使用二叉树,其中包含指向每个节点的左子节点和右子节点的指针。此外,树的叶节点连接起来形成一个双向链表。这种双重性允许高效地遍历和操作该结构。

递归包括对二叉树进行深度优先遍历以生成一个大树链表。从根节点开始,该算法递归地遍历左子树,通过更新其指针以形成链表段来处理当前节点,然后继续遍历右子树。此过程持续进行,直到所有节点都被遍历并相互连接。

大树链表递归的真正强大之处在于它能够执行受益于树状分层组织和链表状顺序访问的操作。例如,使用二叉树结构可以高效地实现元素的搜索、插入和删除,而需要顺序处理的任务则可以利用链表结构。

注意:我强烈建议您在查看解释之前提出一种方法

大树链表递归代码

Java 代码

输出

Converted Doubly Linked List:
1 2 3 4 5 6 7

说明

代码工作原理

该代码创建一个二叉搜索树,使用大树链表递归概念将其原地转换为双向链表,然后打印链表。convertToLinkedList 方法递归遍历树,更新指针以创建链表结构。

时间复杂度

该算法的时间复杂度为 O(n),其中 n 是二叉树中节点的数量。这是因为在转换过程中每个节点都被访问一次。

空间复杂度

空间复杂度为 O(h),其中 h 是二叉树的高度。这是由于栈上的递归调用。当树倾斜且高度为 n(不平衡)时,空间复杂度可能为 O(n)。当树平衡时,空间复杂度将接近 O(log n)。

空运行

主方法

  • 创建具有上述结构的二叉搜索树。
  • 创建 TreeToListConversion 的实例。
  • 使用树的根节点调用 convertToLinkedList 方法。

convertToLinkedList 方法

  • 初始状态:prev = null
  • 从根节点(值 4)开始
  • 递归调用左子树(值为 2 的节点)上的 convertToLinkedList。
  • 递归调用左子树(值为 1 的节点)上的 convertToLinkedList。
  • 由于没有左子树,所以什么也不会发生。
  • 更新 prev 以指向值为 1 的节点。
  • 将值为 1 的节点连接到值为 2 的节点的右侧。
  • 更新 prev 以指向值为 2 的节点。
  • 将值为 2 的节点连接到值为 4 的节点的右侧。
  • 递归调用右子树(值为 3 的节点)上的 convertToLinkedList。
  • 由于没有左子树,所以什么也不会发生。
  • 更新 prev 以指向值为 3 的节点。
  • 将值为 3 的节点连接到值为 2 的节点的右侧。
  • 递归调用右子树(值为 6 的节点)上的 convertToLinkedList。
  • 递归调用左子树(值为 5 的节点)上的 convertToLinkedList。
  • 由于没有左子树,所以什么也不会发生。
  • 更新 prev 以指向值为 5 的节点。
  • 将值为 5 的节点连接到值为 6 的节点的右侧。
  • 更新 prev 以指向值为 6 的节点。
  • 将值为 6 的节点连接到值为 4 的节点的右侧。
  • 更新 prev 以指向值为 4 的节点。

打印链表

  • 使用最左侧节点(值 1)调用 printList 方法。
  • 循环使用右指针遍历链表并打印值:1 2 3 4 5 6 7。
  • 输出:转换后的双向链表:1 2 3 4 5 6 7

使用栈数据结构的大树链表代码

Java 代码

输出

Converted Doubly Linked List:
1 2 3 4 5 6 7

说明

时间复杂度

该算法的时间复杂度为 O(n),其中 n 是二叉树中节点的数量。每个节点都只访问一次,并且每个节点的压栈/出栈操作都花费常数时间。

空间复杂度

用于模拟迭代过程的栈大小决定了空间复杂度。在最坏情况下,当树倾斜(不平衡)时,空间复杂度可能达到 O(n)。在平衡树中,空间复杂度将接近 O(log n),因为栈的高度由树的高度决定。

空运行

步骤 1:初始化

  • 创建具有上述结构的二叉搜索树。
  • 使用树的根节点调用 convertToLinkedList 方法。
  • 栈为空,当前节点初始化为根节点(值 4)。
  • prev、head 和 current 也设置为 null。

步骤 2:使用栈进行迭代中序遍历

当前节点:4

  • 将节点 4 压入栈中。
  • 移动到左子节点。

当前节点:2

  • 将节点 2 压入栈中。
  • 移动到左子节点。

当前节点:1

  • 将节点 1 压入栈中。
  • 移动到左子节点(null)。
  • 从栈中弹出节点 1。
  • 更新 prev 以指向节点 1。
  • 将节点 1 的右侧连接到节点 2。

当前节点:2

  • 更新 prev 以指向节点 2。
  • 将节点 2 的右侧连接到节点 4。
  • 移动到右子节点。

当前节点:3

  • 将节点 3 压入栈中。
  • 移动到左子节点(null)。
  • 从栈中弹出节点 3。
  • 更新 prev 以指向节点 3。
  • 将节点 3 的右侧连接到节点 2。

当前节点:4

  • 移动到右子节点。
  • 当前节点:6
  • 将节点 6 压入栈中。
  • 移动到左子节点。

当前节点:5

  • 将节点 5 压入栈中。
  • 移动到左子节点(null)。
  • 从栈中弹出节点 5。
  • 更新 prev 以指向节点 5。
  • 将节点 5 的右侧连接到节点 6。

当前节点:6

  • 更新 prev 以指向节点 6。
  • 将节点 6 的右侧连接到节点 4。
  • 移动到右子节点。

当前节点:7

  • 将节点 7 压入栈中。
  • 移动到左子节点(null)。
  • 从栈中弹出节点 7。
  • 更新 prev 以指向节点 7。
  • 将节点 7 的右侧连接到节点 6。

当前节点:null

栈为空,遍历完成。