-
题目:
将一个二叉搜索树按结点值从小到大的顺序转换成循环双向链表;
不增添结点,只修改结点的left,right指针; -
思路:
结点的left充当pre,right充当next;
1.分治:O(n),O(n):当二叉搜索树退化成链表时,递归深度为n层
思想是:将二叉搜索树root看成三部分,root,root的左子树,root的右子树;
通过递归得到root的左子树的最小值节点和最大值结点,将root与其最大值结点双向连接起来;
右侧同理,通过递归得到root的右子树的最小值结点和最大值结点,将root与其最小值结点双向连接起来;
这样就得到了整棵树root的双向链表,最后再把首尾两个结点连接起来即可;
class Solution {
private:
pair<Node*, Node*> travel(Node* root) {
//返回当前子树root的最小节点和最大节点;
//我们确保传入的结点不为空,因此不用判空
if (!root->left && !root->right) return {
root, root};//到了叶子结点,因此最小和最大节点都是自己
pair<Node*, Node*> lp, rp;
//只递归调用非空结点
if (root->left) lp = travel(root->left);
if (root->right) rp = travel(root->right);
Node* min, *max;//当前子树root的最小节点min,最大节点max
//由于root的左右子树都能为空,因此分情况讨论一下,确定min和max
if (lp.first == nullptr) {
//root的左子树为空,则最小值就是root
min = root;
}
else {
//否则最小值就是就是root的左子树的最小值
min = lp.first;
root->left = lp.second;//把root和root的左子树的最大值双向连起来
lp.second->right = root;
}
if (rp.first == nullptr) {
//同上:root的右子树为空,则最大值就是root
max = root;
}
else {
//否则最大值就是root的右子树的最大值
max = rp.second;
root->right = rp.first;//把root和root的右子树的最小值双向连起来
rp.first->left = root;
}
return {
min, max};//返回当前root子树的最小和最大值结点
}
public:
Node* treeToDoublyList(Node* root) {
if (!root) return nullptr;
pair<Node*, Node*> res = travel(root);
//最后把双向链表的首尾连接起来,形成循环双向链表,并返回首结点
res.first->left = res.second;
res.second->right = res.first;
return res.first;
}
};
2.中序遍历:O(n):中序遍历一次,O(n):当二叉树退化成链表时,递归深度最深为n层;
方法1没有很好的利用二叉搜索树自带的顺序这一点;
利用二叉搜索树的中序遍历,边遍历边将当前节点和前驱节点双向连接起来即可构成双向链表;
中序遍历的顺序和预期形成的双向链表的顺序一致;
中序遍历时,当问当前节点cur时,其左子树一定访问过了,因此不用担心双向链表断掉;
因此,维护一个全局变量pre保存当前节点的前一个结点,每次将当前节点cur与pre双向连接起来即可;
class Solution {
private:
Node* pre, *head;
void travel(Node* root) {
//在常规的中序遍历基础上,修改中所做的操作即可;
if (!root) return;
travel(root->left);//左
if (!pre) head = root;//root是双向链表的首届结点,即二叉搜索树的最小值结点
else {
//否则,需要将root和其前驱pre双向连接起来
root->left = pre;
pre->right = root;
}
pre = root;
travel(root->right);//右
}
public:
Node* treeToDoublyList(Node* root) {
if (!root) return nullptr;
pre = nullptr;
travel(root);
//连接首:head 尾:pre
head->left = pre;
pre->right = head;
return head;
}
};
- 总结:
方法1巧妙需多用,方法2更常规容易理解;