剑指Offer36.二叉搜索树与双向链表

  • 剑指Offer36.二叉搜索树与双向链表

  • 题目:
    将一个二叉搜索树按结点值从小到大的顺序转换成循环双向链表;
    不增添结点,只修改结点的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更常规容易理解;

猜你喜欢

转载自blog.csdn.net/jiuri1005/article/details/114040127