我正在处理以下面试问题:
给出一个单链列表,其中元素按升序排序 订单,将其转换为高度平衡的BST。
对于此问题,将高度平衡的二叉树定义为二叉树 每个节点的两个子树的深度从不变化的树 超过1。
我试图了解以下解决方案及其复杂性?有人可以帮助我了解其运作方式吗?下面的解是否为O(n)时间复杂度和O(log n)空间复杂度?
下面的算法也比“计算给定的链表中的节点数更好。让它为n。在计数节点之后,我们取左n / 2个节点并递归构造左子树。构造左子树后,我们为根分配内存,并将左侧的子树与根链接,最后,递归构造右侧的子树,并将其与根链接。在构造BST时,我们还不断将列表头指针移至下一个,以便在其中具有适当的指针。每个递归调用”
public TreeNode toBST(ListNode head) {
if(head==null) return null;
return helper(head,null);
}
public TreeNode helper(ListNode head, ListNode tail){
ListNode slow = head;
ListNode fast = head;
if(head==tail) return null;
while(fast!=tail && fast.next!=tail){
fast = fast.next.next;
slow = slow.next;
}
TreeNode thead = new TreeNode(slow.val);
thead.left = helper(head,slow);
thead.right = helper(slow.next,tail);
return thead;
}
答案 0 :(得分:3)
可以通过将列表细分为两个等长的列表(其中中间的一个元素用作根),从排序后的列表构建平衡树。例如:
1. [1, 2, 3, 4, 5, 6, 7]
2. 4
/ \
[1, 2, 3] [5, 6, 7]
3. 4
/ \
2 6
/ \ / \
1 3 5 7
即使两个子列表相差一个元素,它们的高度最多也可以相差1,从而使树保持平衡。通过使用列表的中间元素,可以确保生成的树是BST,因为所有较小的元素都是左子树的一部分,而所有较大的元素都是右子树的所有元素。
slow
和fast
您的代码使用两个迭代器工作,其中一个(fast
)在节点上的迭代速度是另一个(slow
)的两倍。因此,当fast
到达列表尾部或尾部之前的节点时,slow
必须位于列表中间的节点处,从而将列表分为两个相同长度的子列表(最多不超过一个元素差异),然后可以按上图所示进行递归处理。
该算法在O(n lg n)
中运行。让我们从辅助程序的重复开始:
T(n) = n / 2 + 2 * T(n / 2)
T(1) = 1
在每次helper
调用中,我们必须找到由传递给helper
的两个参数定义的链表的中间节点。这只能通过n / 2
的步骤来完成,因为我们只能线性地遍历列表。另外,在原始列表大小的一半的链接列表上递归调用helper
两次,以构建左右子树。
将Master-theorem(案例2)应用于上述重复发生,我们得到O(n lg n)
。
空间复杂性还需要考虑产生的输出结构。由于输入链接列表的每个元素都转换为BST中的一个节点,因此复杂度为O(n)
。
编辑
如果忽略输出,则空间复杂度仅取决于递归深度,而递归深度又为O(lg n)
,因此使空间复杂度为O(lg n)
。