二叉搜索树与双向链表(解题思路)

题目

来源: Leetcode 剑指 Offer 36. 二叉搜索树与双向链表
问题: 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。


DFS旋转 + 一趟遍历维护链表

基本思路

核心: "二叉搜索树的中序遍历是有序的。

我们可以通过左旋转,将每棵子树的右孩子都甩到左边,仍能保持二叉搜索树的性质不变。(当然, 也可以使用右旋转,思路都是一样的)

因此, 只需要递归遍历所有节点,并按照上述方法改造二叉搜索树,即可获得有序的单链表。

最后只需要一趟遍历,维护双向链表和循环链表的性质。

实现代码

class Solution {

public:
    // 左旋转,返回新的父节点
    Node* left_rotate(Node* n){
        Node* root = n->right;
        n->right = root->left;
        root->left = n;
        return root;
    }

    Node* dfs(Node* n){
        if(!n) return nullptr;
        
        // 一直左旋转到没有右孩子
        while(n->right)
            n = left_rotate(n);
        
        // 更新前驱
        n->left = dfs(n->left);

        return n;
    }

    Node* treeToDoublyList(Node* root) {
        if(!root) return nullptr;

        Node* rear = dfs(root);
        Node* pre = rear;
        root = rear->left;
        

        // 维护双向链表属性
        while(root){
            root->right = pre;
            pre = root;
            root = root->left;
        }

        // 维护循环列表属性
        if(pre != rear){
            root = pre;
            root->left = rear;
            rear->right = root;
        }else{
            root = rear;
            rear->right = rear;
            rear->left  = rear;
        }

        return root;
    }
};

后序遍历同时构建链表

基本思路

考虑到二叉搜索的特点:

左子树中的所有值 < 根 < 右子树中的所有值

既然如此,若左子树中的值和右子树中的值已经构造成有序的链表时,根只需要链接到左右子树之间就好了,不仅如此该函数从该特性出发就是符合递归的特点的。

为了方便链接,每个节点维护部分区间的链表的头尾节点。

orignal
1
4
2
5
3
1,2,3
4
5

代码实现

#define head(x)  get<0>((x))
#define rear(x)  get<1>((x))

tuple<Node*, Node*> dfs(Node* root) {
    tuple<Node*, Node*> l = make_tuple(root, root);
    tuple<Node*, Node*> r = make_tuple(root, root);
    tuple<Node*, Node*> res;

    if (root->left && root->right) {
        l = dfs(root->left);
        r = dfs(root->right);

        // 根节点与左有序双向链表链接
        root->left = rear(l);
        rear(l)->right = root;
        // 根节点与右有序双向链表链接
        root->right = head(r);
        head(r)->left = root;

        // 维护循环链表特性
        head(l)->left = rear(r);
        rear(r)->right = head(l);

        res = make_tuple(head(l), rear(r));
    }
    else if (root->left) {
        // 根节点与左有序双向链表链接
        l = dfs(root->left);
        root->left = rear(l);
        rear(l)->right = root;

        // 维护循环链表特性
        head(l)->left = root;
        root->right = head(l);

        res = make_tuple(head(l), root);
    }
    else if (root->right) {
        // 根节点与右有序双向链表链接
        r = dfs(root->right);
        root->right = head(r);
        head(r)->left = root;

        // 维护循环链表特性
        root->left = rear(r);
        rear(r)->right = root;

        res = make_tuple(root, rear(r));
    }
    else {
        root->left = root;
        root->right = root;
        res = make_tuple(root, root);
    }

    return res;
}

Node* treeToDoublyList(Node* root) {
    if (!root) return nullptr;

    return head(dfs(root));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值