题目
- 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。
- 将循环双向链表中最小元素作为根节点,并返回。
二叉搜索树定义
- 或者是一棵空树,或者是具有下列性质的二叉树: (来源:二叉搜索树百度百科)
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树。
- 特点:其中序遍历的结果是一个有序序列,即按左、中、右的顺序遍历该树,得到升序序列;按右、中、左的顺序遍历,将得到降序序列(见剑指 Offer 54. 二叉搜索树的第k大节点)。
题解
-
通过对该二叉搜索树的中序遍历可以得到排序序列。
// 中序遍历 const inorderTravel = (root) => { if (!root) { return } // 左 inorderTravel(root.left) // 中 handleCurNode(root) // 右 inorderTravel(root.right) }
-
对于某节点curNode和其前驱节点preNode,分别按以下规则修改指针指向,即可得到双向链表;修改头、尾节点的指针指向,得到循环链表。
- preNode.right = curNode
- curNode.left = preNode
// 前驱节点为null,该节点是中序遍历序列中最小的节点,作为头节点
if (!preNode) {
head = root
}
if (preNode) {
preNode.right = root
}
root.left = preNode
// 更新前驱节点,进入下一个节点的处理
preNode = root
var treeToDoublyList = function(root) {
let preNode = null
let head = null
const inorderTravel = (root) => {
if (!root) { return }
inorderTravel(root.left)
if (!preNode) {
head = root
}
if (preNode) {
preNode.right = root
}
root.left = preNode
preNode = root
inorderTravel(root.right)
}
inorderTravel(root)
// head和最后一个仍然没有双向绑定
head.left = preNode
preNode.right = head
return head
};
- 复杂度分析
- 时间复杂度:与树的高度有关
- 完全二叉树是最优效率,时间复杂度为O(lgn)
- 退化为链表,即只有左子树或只有右子树时,时间复杂度为O(n)
- 空间复杂度:同理,若为完全二叉树,递归O为(lgn),开辟O(lgn)栈空间;退化为链表,递归深度为O(n),开辟O(n)栈空间。