二叉搜索树与双向链表[成员变量记录前驱+ 中序遍历 || 中序遍历 + 局部变量 + 回溯]

前言

一题多解,才能打开格局和视野,多角度刨析问题,才能举一反三。
如:二叉搜索树如何展开成双向链表?
M1:通过中序遍历二叉树即可,把每个节点接入全局变量的链表尾指针。
M1-defect:需要一个成员变量来帮助。
那直接在中序遍历中可否操作?
key-problem:中序遍历不能做到随时拿到新的tail指针,则可通过对象数组方式来解决。
key-problem:中序遍历不借助tail指针或数组,是不能可能解决问题的。可否不用中序遍历解决?
刨析二叉搜索树的特点:
二叉搜索树特点,每个节点的前驱为其左子树的最右节点,每个节点的后继为其右子树的最左节点。所以可以返回一个Node数组存储子树的最左右节点即可。

一、二叉搜索树与双向链表

在这里插入图片描述
在这里插入图片描述

二、中序遍历

1、中序遍历+成员变量

//二叉搜索树与双向链表
public class TreeToDoublyList {
    /*
    target:按照二叉搜索树的节点顺序将节点以双向循环链表的方式串起来。
    顺序性?通过中序遍历即可。
    双向链表?通过记录pre节点+不断更新pre节点+pre 与 cur节点left、right操作。
    循环?不断left找到头节点,中序遍历可以得到尾节点,然后设置以下left、right即可。
     */
    public Node treeToDoublyList(Node root) {
        if (null == root) return null;
        //1-寻找起始节点。
        Node head = null, cur = root;
        while (cur.left != null) cur = cur.left;
        head = cur;
        //递归回溯来改变链的结构。
        dfs(root, pre);
        //双向循环
        head.left = pre;
        pre.right = head;
        //返回头节点
        return head;
    }

    Node pre = new Node();//传入的pre是一个地址,但是我们要更新的不是地址的值,而是pre本身的指向。

    private void dfs(Node root, Node pre) {
        if (root == null) return;
        //回溯改变链的结构
        dfs(root.left, pre);

        pre.right = root;
        root.left = pre;
        pre = root;

        dfs(root.right, pre);

    }

    // Definition for a Node.
    class Node {
        public int val;
        public Node left;
        public Node right;

        public Node() {
        }

        public Node(int _val) {
            val = _val;
        }

        public Node(int _val, Node _left, Node _right) {
            val = _val;
            left = _left;
            right = _right;
        }
    }


}

2、中序遍历+非成员数组

class TreeToDoublyList2 {
    /*
    target:按照二叉搜索树的节点顺序将节点以双向循环链表的方式串起来。
    顺序性?通过中序遍历即可。
    双向链表?通过记录pre节点+不断更新pre节点+pre 与 cur节点left、right操作。
    循环?不断left找到头节点,中序遍历可以得到尾节点,然后设置以下left、right即可。
     */
    public Node treeToDoublyList(Node root) {
        if (null == root) return null;
        //1-寻找起始节点。
        Node head = null, cur = root;
        while (cur.left != null) cur = cur.left;
        head = cur;
        Node[] pre = new Node[]{new Node()};//把tail指针当作一个值来更新。
        //递归回溯来改变链的结构。
        dfs(root, pre);
        //双向循环
        head.left = pre[0];
        pre[0].right = head;
        //返回头节点
        return head;
    }



    private void dfs(Node root, Node[] pre) {
        if (root == null) return;
        //回溯改变链的结构
        dfs(root.left, pre);

        pre[0].right = root;
        root.left = pre[0];
        pre[0] = root;

        dfs(root.right, pre);

    }

    // Definition for a Node.
    class Node {
        public int val;
        public Node left;
        public Node right;

        public Node() {
        }

        public Node(int _val) {
            val = _val;
        }

        public Node(int _val, Node _left, Node _right) {
            val = _val;
            left = _left;
            right = _right;
        }
    }


}

3、递归回溯–后序遍历处理每一个root

class TreeToDoublyList3 {
    /*
    target:按照二叉搜索树的节点顺序将节点以双向循环链表的方式串起来。
    如何用局部变量的方式实现?
    不用全局变量的一个问题就是:如何在中序遍历中拿到一个节点点的前后节点?
    S:每个 root节点的前驱是左子树的最右孩子,root节点的后继是右子树的最左孩子。
    所以,以回溯的方式返回子树的最左节点和最右节点。然后root就可以取它的前驱和后继。
     */
    public Node treeToDoublyList(Node root) {
        if (null == root) return null;
        //root树最左和最右节点。
        Node[] lt_rt = dfs(root);
        //双向循环
        lt_rt[0].left = lt_rt[1];
        lt_rt[1].right = lt_rt[0];
        //返回头节点
        return lt_rt[0];
    }

    private Node[] dfs(Node root) {
        //叶子节点的最左最右节点应该都是它自身
        if (root == null) return null;

        Node[] left = dfs(root.left);
        Node[] right = dfs(root.right);

        Node[] rs = new Node[]{root, root};
        //叶子节点的最左最右都是它自己。
        if (left == null && null == right) return rs;
        //左子树不为空,应该把左子树的最右节点和root的left相关。
        if (left != null) {
            root.left = left[1];
            left[1].right = root;

            rs[0] = left[0];
        }
        if (right != null) {
            root.right = right[0];
            right[0].left = root;

            rs[1] = right[1];
        }
        return rs;
    }

    // Definition for a Node.
    class Node {
        public int val;
        public Node left;
        public Node right;

        public Node() {
        }

        public Node(int _val) {
            val = _val;
        }

        public Node(int _val, Node _left, Node _right) {
            val = _val;
            left = _left;
            right = _right;
        }
    }


}

总结

1)根据二叉搜索树的特点,用后序遍历解决。
2)中序遍历 配合 给引用整个引用----引用数组方式。
3)递归回溯

参考文献

[1] LeetCode 二叉搜索树与双向链表

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值