就地将 BST 转换为最小堆

2025年3月17日 | 阅读 7 分钟

问题陈述

将二叉搜索树转换为具有相同元素的最小堆,采用就地方法,并以线性时间复杂度 (O(n)) 完成此转换。

输入

输出

       8
     /    \
   10       17
 /   \     /   \
15   12   20    25

同样,答案可以是任何符合最小堆原则的内容。

注意:在查看解决方案之前,请先尝试提出一种方法

方法1:朴素方法

使用额外空间

  1. 创建一个大小为N的数组arr,其中N表示给定BST中的节点数。
  2. 执行BST的中序遍历,将节点值按排序顺序存储在数组arr中。
  3. 继续进行树的先序遍历。
  4. 在先序遍历过程中遍历根节点时,系统地将数组arr中的值分配给BST的节点,确保每个节点的值都遵循最小堆所需的顺序。

伪代码

注意:尝试在不查看原始代码的情况下实现,通过参考上述伪代码来提高您的编码水平,如果遇到困难,请对照提供的解决方案来审查多种编写代码的方式。

通过采用这种方法,我们确保二叉搜索树根据指定的条件转换为最小堆,相应地保持元素的完整性和顺序。

上述方法的Java实现

输出

In place, convert BST into a Min-Heap

说明

  • BSTToMinHeap方法递归地使用先序遍历将BST转换为最小堆。
  • 它首先将ArrayList arr中索引i处的值分配给当前节点,然后更新索引(i[0]是一个数组,用于在递归调用中保持可变索引)。
  • 然后它递归地处理左子树和右子树。
  • convertToMinHeapUtil方法作为实用函数,它初始化一个ArrayList来存储节点值,并通过调用inorderTraversal填充ArrayList和BSTToMinHeap执行转换来启动转换过程。

时间复杂度:O(N)

空间复杂度: O(N)

由于上述方法使用了辅助空间,因此您需要进一步优化解决方案。

方法2:(最优)

当允许使用额外内存时,我们可以对树执行中序遍历,并将键记录在一个单独的数组中。由于BST的特性,该数组将自然排序。随后,我们可以通过遍历排序数组并从左到右逐级构造节点来创建一棵完全二叉树。通过从排序数组中选择下一个最小元素,我们保证生成的二叉树遵循最小堆属性。此方法的时间复杂度为O(n),但需要额外的空间用于数组存储,使其成为非就地解决方案。

为了实现二叉搜索树就地转换为排序链表,我们采用逆中序遍历。这包括以优先右子树而非左子树的方式遍历BST。在遍历过程中,我们将节点插入到现有链表的开头,并相应地调整头指针。通过执行此逆中序遍历并仔细管理节点插入,我们保持链表的排序顺序。此方法确保了内存的有效使用,而不依赖于额外的数据结构。

随后,我们继续将排序链表转换为最小堆。这种转换涉及仔细调整节点的左右指针以符合最小堆属性。为了实现这一点,我们对部分构造的最小堆树进行层序遍历。同时,我们同步遍历链表。在每一步中,我们将队列中的父节点指定为下一个两个来自链表的节点作为其子节点。然后将这些新分配的节点入队以进行进一步处理。这个过程在链表排序性质的支持下,保证了最小堆属性的保留。

希望您理解这种方法;下面是详细的算法,以供您的逻辑理解

BST就地转换为最小堆的算法

步骤 1:定义节点结构

定义一个具有整数数据以及指向左右子节点的指针的Node结构。

步骤 2:定义实用函数

定义一个实用函数newNode(data)来创建一个具有给定数据的新节点,将左右指针初始化为NULL。

步骤 3:将BST转换为排序链表

定义一个函数BSTToSortedLL(root, head_ref)来使用逆中序遍历将BST转换为排序链表。

基本情况:如果root为NULL,则返回。

递归调用BSTToSortedLL(root.right, head_ref)。

更新指针以将树转换为链表。

步骤 4:将排序链表转换为最小堆

定义一个函数SortedLLToMinHeap(root, head)来使用队列将排序链表转换为最小堆。

基本情况:如果head为NULL,则返回。

初始化一个队列。

将第一个节点作为最小堆的根节点开始。

遍历链表并使用队列设置左右子指针。

上述方法的Java实现

输出

In place, convert BST into a Min-Heap

时间复杂度:O(n)

辅助空间: O(n)