9.树

一.BiNode
1.题目描述:
二叉树数据结构TreeNode可用来表示单向链表(其中left置空,right为下一个链表节点)。实现一个方法,把二叉搜索树转换为单向链表,要求依然符合二叉搜索树的性质,转换操作应是原址的,也就是在原始的二叉搜索树上直接修改。
返回转换后的单向链表的头节点。
注意:本题相对原题稍作改动

2.示例:
输入: [4,2,5,1,3,null,6,0]//输入是将二叉树以数组的形式表示的,即从上到下从左到右依次填入数组中
输出: [0,null,1,null,2,null,3,null,4,null,5,null,6]

3.解:
(1)我的答案:

class Solution {
    private TreeNode tempPre = null;
    private TreeNode head = null;

    public TreeNode convertBiNode(TreeNode root) {
        if (root == null) return null;
        midTravel(root);
        return head;
    }

    public void midTravel(TreeNode root) {
        //左节点递归
        if (root.left != null) midTravel(root.left);
        //对当前节点的操作

        //获取新树的头节点,以及初次绑定tempPre
        if (root.left == null && tempPre == null) {
            head = root;
            tempPre = root;
        } else {
            tempPre.right = root;
            root.left = null;
            tempPre = root;
        }
        //右节点递归
        if (root.right != null) midTravel(root.right);
    }
}

(2)网友答案:

public TreeNode convertBiNode(TreeNode root) {
        TreeNode head = new TreeNode(0);// 单链表的头指针哨兵
        TreeNode prev = head;// 移动的链表前置指针
        // 开始中序遍历
        TreeNode node = root;
        Deque<TreeNode> stack = new LinkedList<>();
        while (node != null || !stack.isEmpty()){
            if (node != null){
                stack.push(node);
                node = node.left;
            }else {
                node = stack.pop();
                // ---链表处理
                node.left = null;// 左指针置空
                prev.right = node;// 右指针作为链表的next指针
                prev = node;// 指针后移
                // ---链表处理
                // 中序遍历进入右子树
                node = node.right;
            }
        }
        return head.right;
    }

4.总结
以上一种是使用递归,一种是非递归,非递归的方式采用栈这种数据结构,看代码即可看懂。

二.二叉搜索树的最近公共祖先-i
1.题目描述:
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
图片说明

2.示例:
输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6
解释: 节点 2 和节点 8 的最近公共祖先是 6。

3.解:
(1)网友答案:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null;
        if(root.val>p.val&&root.val>q.val) return lowestCommonAncestor(root.left,p,q);
        if(root.val<p.val&&root.val<q.val) return lowestCommonAncestor(root.right,p,q);
        return root;     
    }
}
class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        # 如果当前节点为空,就返回空
        if not root:
            return None
        # 如果当前结点就是p或者q,就返回当前节点
        if root in [p, q]:
            return root

        # 分别去左右子树里面去找
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)

        # 如果左右子树里各有一个p或者q,那么当前就是最前结点
        if left and right:
            return root

        # 如果都在左边,那就是当前这个左边的
        elif left:
            return left

        # 要么就是右边的
        elif right:
            return right

        return None

4.总结
简洁明了,利用二叉搜索树的性质,即每个节点的值必须大于等于其左节点的值且必须小于等于其右节点的值。因此就可以使用前,中,后序遍历其中一种进行递归,循环查找,直到找到目标节点。网友1的答案使用的是后序遍历。
第二个答案同样也是对的,这种方法既适用于搜索二叉树,也适用于普通的二叉树。该方法不再是通过节点的值来决定,接下来往左边还是右边走。而是每到一个节点,左右两条路都走,然后每条路会返回一个节点,如果某条路中走下去可以找到p和q其中一个,(当然也存在p和q都可以找到的情况,那样的话目标节点就在当前节点内了)那么该条路就是有返回值的,然后若两条路都有返回值,那么这个节点就是目标节点。单反有一条路没有返回值,那么目标节点一定比当前节点的层数高。

三.将二叉搜索树变平衡
1.题目描述:
给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。

如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是 平衡的 。

如果有多种构造方法,请你返回任意一种。

2.示例:
图片说明

图片说明

输入:root = [1,null,2,null,3,null,4,null,null]
输出:[2,1,3,null,null,null,4]
解释:这不是唯一的正确答案,[3,1,4,null,2,null,null] 也是一个可行的构造方案。

3.解:
(1)我的答案:

class Solution {
    List<TreeNode> newTree = new ArrayList<>();

    public TreeNode balanceBST(TreeNode root) {
        if (root == null) return null;
        else midTravel(root);
        return rebuild(0, newTree.size() - 1);
    }

    private void midTravel(TreeNode root) {
        if (root.left != null) midTravel(root.left);
        newTree.add(root);
        if (root.right != null) midTravel(root.right);
    }

    private TreeNode rebuild(int low, int high) {
        if(high < low) return null;
        int mid = low + ((high - low) >> 1);
        TreeNode root = newTree.get(mid);
        root.left = rebuild(low, mid - 1);
        root.right = rebuild(mid + 1, high);
        return root;
    }
}

(2)网友答案:

class Solution {
    private final int[] array = new int[100001];
    private int arrayIndex = 0;

    public TreeNode balanceBST(TreeNode root) {
        midTraverse(root);
        return build(0, arrayIndex - 1);
    }

    public void midTraverse(TreeNode cur) {
        if (cur == null) {
            return;
        }
        midTraverse(cur.left);
        array[arrayIndex++] = cur.val;
        midTraverse(cur.right);
    }

    public TreeNode build(int left, int right) {
        int mid = left + ((right - left) >> 1);//相当于 left + (right - left) / 2 不写成(left+right)/2是为了防止(left+right)溢出
        TreeNode cur = new TreeNode(array[mid]);
        if (left < mid) {
            cur.left = build(left, mid - 1);
        }
        if (mid < right) {
            cur.right = build(mid + 1, right);
        }
        return cur;
    }
}

4.总结
先将每个节点存储到一个链式结构中,然后用二分查找,找到链式结构中最中间位置的元素作为重构树的根节点,然后分别在左半部分和右半部分递归重构该根节点的左右子树,直到重构完成。

四. 二叉搜索子树的最大键值和
1.题目描述:
给你一棵以 root 为根的 二叉树 ,请你返回 任意 二叉搜索子树的最大键值和。

二叉搜索树的定义如下:

任意节点的左子树中的键值都 小于 此节点的键值。
任意节点的右子树中的键值都 大于 此节点的键值。
任意节点的左子树和右子树都是二叉搜索树。

2.示例:
图片说明

输入:root = [1,4,3,2,4,2,5,null,null,null,null,null,null,4,6]
输出:20
解释:键值为 3 的子树是和最大的二叉搜索树。

图片说明
输入:root = [4,3,null,1,2]
输出:2
解释:键值为 2 的单节点子树是和最大的二叉搜索树。
示例 3:

输入:root = [-4,-2,-5]
输出:0
解释:所有节点键值都为负数,和最大的二叉搜索树为空。

3.解
(1)我的答案:

class Solution {
    private int result = Integer.MIN_VALUE;

    public int maxSumBST(TreeNode root) {
        if (root == null) return 0;
        subTravel(root);
        return result < 0 ? 0 : result;
    }

    private int[] subTravel(TreeNode root) {
        if (root == null) return new int[]{1, 0, Integer.MIN_VALUE, Integer.MAX_VALUE};//位置2代表该树所有节点的最大值,位置3代表。。最小值
        int[] left = subTravel(root.left);
        int[] right = subTravel(root.right);
        int[] temp = new int[4];
        if (left[0] == 1 && right[0] == 1 && left[2] < root.val && right[3] > root.val) {
            temp[0] = 1;
            temp[1] = left[1] + right[1] + root.val;
            if (right[2] == Integer.MIN_VALUE && right[3] == Integer.MAX_VALUE) temp[2] = root.val;
            else temp[2] = right[2];
            if (left[2] == Integer.MIN_VALUE && left[3] == Integer.MAX_VALUE) temp[3] = root.val;
            else temp[3] = left[3];
            result = Math.max(result, temp[1]);
        }
        return temp;
    }
}

4.总结:
没有必要遍历两次,(遍历两次的做法是,建立一个HashMap<TreeNode,int[3]>第一次遍历获取每个节点的总值和最大值,最小值。第二次遍历判断每个节点作根节点的树是否是二叉搜索树(通过判断两个子树是不是二叉搜索树,并判断节点值小于左子树最大值且小于右子树最小值来确定当前节点作根节点的树是不是二叉树),是的话就更新结果最大值。最后两次遍历完之后,返回结果最大值)一次遍历即可。只需要令递归函数的返回值是一个长度为4的数组就行,这4个元素分别存储:节点的总值/所有子节点和自身节点中的最大值/所有子节点和自身节点中的最小值/是不是二叉搜索树。其中一个细节是,若子树为空,则也创建一个长度为4的数组,仿佛有子树一样。

全部评论

相关推荐

09-14 14:42
门头沟学院 C++
旺旺米雪饼:举办了哥,你什么都没做错,全怪我那令人作呕的嫉妒和卑微的自尊心,看见你的文字我完全破防了,我直接丢盔弃甲了,看见你这图的那一秒,我满头大汗,浑身发冷,亿郁症瞬间发作了,生活仿佛没了颜色,像是被抓住尾巴的赛亚人,带着海楼石的能力者,抽离尾兽的人柱力,像是没了光的奥特曼,彻底断绝了生的希望。我几乎都快羡慕得疯了,倒在床上蒙住被子就开始抱着枕头尖叫流泪,嘴里一边喊着卧槽卧槽,一边又忍着,我边发边哭,打字的手都是抖的,后来我的手抖得越来越厉害,从心头涌起的思想、情怀和梦想,这份歆羡和悔恨交织在一起,我的笑还挂在脸上,可是眼泪一下子就掉下来了。求你了别发了,我生活再难再穷我都不会觉得难过,只有你们发这种东西的时候,我的心里像被刀割一样的痛,打着字泪水就忍不住的往下流。每天早上6点起床晚上11点睡觉,年复一年地学到现在,憧憬着一个月赚上万块的幸福生活,憧憬着美好阳光的未来。我打开了手机,看到你的图,我感到了深深的差距,我直接跳进了家门口的井里😭😭😭我真的😭我要嫉妒疯了😭为什么!!为什么这个人不是我😡我求你了😭求你了😭!不要在发了,我真的要羡慕嫉妒疯了😱怎么办我要嫉妒死了啊啊啊啊我急了,手机电脑全砸了,本来就有抑郁症的我,被别人说我破防了,我真的恼羞成怒了,仿佛被看穿了,躲在网络背后的我,这种感觉真的好难受,我被看穿的死死地,短短的破防两个字,我伪装出来的所有的坚强和强颜欢笑全都崩塌了,成了一个被人笑话的小丑🤡,我真的不想再故作坚强了,玩心态我输的什么都不剩😭😭😭
点赞 评论 收藏
分享
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务