872. 叶子相似的树 / 剑指 Offer 33. 二叉搜索树的后序遍历序列 / 剑指 Offer 34. 二叉树中和为某一值的路径 / 剑指 Offer 35. 复杂链表的复制

872. 叶子相似的树

2021.5.10 每日一题

题目描述
请考虑一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。

在这里插入图片描述

举个例子,如上图所示,给定一棵叶值序列为 (6, 7, 4, 9, 8) 的树。

如果有两棵二叉树的叶值序列是相同,那么我们就认为它们是 叶相似 的。

如果给定的两个根结点分别为 root1 和 root2 的树是叶相似的,则返回 true;否则返回 false 。

在这里插入图片描述

输入:root1 = [3,5,1,6,2,9,8,null,null,7,4], root2 = [3,5,1,6,7,4,2,null,null,null,null,null,null,9,8]
输出:true
示例 2:

输入:root1 = [1], root2 = [1]
输出:true
示例 3:

输入:root1 = [1], root2 = [2]
输出:false
示例 4:

输入:root1 = [1,2], root2 = [2,2]
输出:true
示例 5:

输入:root1 = [1,2,3], root2 = [1,3,2]
输出:false

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/leaf-similar-trees
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路
class Solution {
    public boolean leafSimilar(TreeNode root1, TreeNode root2) {
        //深度优先搜索到两棵树的子节点,然后比较,想遍历过程中比较的,感觉不太行,还是存起来比较吧
        List<Integer> list1 = new ArrayList<>();
        List<Integer> list2 = new ArrayList<>();
        dfs(root1, list1);
        dfs(root2, list2);
        if(list1.size() != list2.size())
            return false;
        for(int i = 0; i < list1.size(); i++){
            if(list1.get(i) != list2.get(i))
                return false;
        }        
        return true;
    }

    public void dfs(TreeNode root, List<Integer> list){
        if(root.left == null && root.right == null){
            list.add(root.val);
        }
        if(root.left != null)
            dfs(root.left, list);
        if(root.right != null)
            dfs(root.right, list);
    }
}

剑指 Offer 33. 二叉搜索树的后序遍历序列

题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

参考以下这颗二叉搜索树:

     5
    / \
   2   6
  / \
 1   3
示例 1:

输入: [1,6,3,2,5]
输出: false
示例 2:

输入: [1,3,2,6,5]
输出: true

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

根据后序遍历的结果特点来把数组拆成左右根三个部分,然后对应的部分再进行划分,知道区间只剩下一个节点或者区间不存在

class Solution {
    int[] postorder;
    public boolean verifyPostorder(int[] postorder) {
        //后序遍历结果,左右根,然后呢,对,还有个条件是二叉搜索树,二叉搜索树中序遍历的结果是一个递增序列
        //后序遍历最后一个数字是根节点,然后小于它的是左,大于它的是右,然后再分,在判断
        //好像就是这么个思路
        this.postorder = postorder;
        int l = postorder.length;
        return helper(0, l - 1, Integer.MIN_VALUE, Integer.MAX_VALUE);
    }

    public boolean helper(int start, int end, int min, int max){
        if(start >= end){
            return true;
        }
        int root = postorder[end];
        if(root < min || root > max)
            return false;
        //找到左右不分的分界点,也就是第一个大于root的点
        int mid = -1;
        for(int i = start; i < end; i++){
            if(postorder[i] < min)
                return false;
            if(postorder[i] > root){
                mid = i;
                break;
            }
        }
        //判断分界点后的数是否全部大于root
        if(mid == -1)
            mid = end;
        for(int i = mid; i < end; i++){
            if(postorder[i] < root || postorder[i] > max)
                return false;
        }
        //继续判断子集
        return helper(start, mid - 1, min, root - 1) && helper(mid, end - 1, root + 1, max);

    }
}

看题解以后,发现写的还是麻烦,我这里加了最大最小值的限制,我认为是没问题的,但是好像没有也行?因为在每次划分左右的时候已经满足大小条件了

class Solution {
    public boolean verifyPostorder(int[] postorder) {
        return recur(postorder, 0, postorder.length - 1);
    }

    //判断i到j是否是一个二叉搜索树
    public boolean recur(int[] postorder, int i, int j){
        if(i >= j)
        //如果只有一个节点,那么肯定是
            return true;
        //然后找出分界点
        int m = i;
        //如果小于根节点,就一直加
        while(postorder[m] < postorder[j])
            m++;
        //到这里找到了分界点
        int p = m;
        //因为右子树都比根节点大
        while(postorder[p] > postorder[j])
            p++;
        //p不等于j就说明不是
        return p == j && recur(postorder, i, m - 1) && recur(postorder, m, j - 1);
    }
}

剑指 Offer 34. 二叉树中和为某一值的路径

题目描述
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。

示例:
给定如下二叉树,以及目标和 target = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
返回:

[
   [5,4,11,2],
   [5,8,4,5]
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

深度搜索遍历每一条路径就行了,最后看路径和是否和target相同,注意因为没有给定每个节点的值的大小范围,因此不能随便剪枝

class Solution {
    List<List<Integer>> res;
    List<Integer> list;
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        //又得回溯了,这里叶节点应该是叶子结点,即没有子节点的结点
        res = new ArrayList<>();
        list = new ArrayList<>();
        recur(root, target, 0);
        return res;
    }

    public void recur(TreeNode root, int target, int sum){
        if(root == null)
            return;
        if(root.left == null && root.right == null){
            //加起来等于target
            if(sum + root.val == target){
                list.add(root.val);
                res.add(new ArrayList<>(list));
                list.remove(list.size() - 1);
                return;
            }
            return;
        }
        list.add(root.val);
        recur(root.left, target, sum + root.val);
   //     list.remove(list.size() - 1);

     //   list.add(root.val);
        recur(root.right, target, sum + root.val);
        list.remove(list.size() - 1);
        
    }
}

连续做了几道树的题吧,感觉对递归回溯更加熟练了,最起码自己能写出来了,是个进步,不过还得加油啊

剑指 Offer 35. 复杂链表的复制

题目描述
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

示例 1:
在这里插入图片描述

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:
在这里插入图片描述

输入:head = [[1,1],[2,1]]
输出:[[1,1],[2,1]]


示例 4:

输入:head = []
输出:[]
解释:给定的链表为空(空指针),因此返回 null。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

一时间没想到思路,想到的是最笨的方法,然后看到哈希表,略有所悟

class Solution {
    public Node copyRandomList(Node head) {
        //这个怎么做呢,第一遍遍历构建所有节点,问题是random怎么处理,难道要一个节点一个节点的处理吗
        //感觉我实在是想不到更好的方法了,感觉一个个处理也很难写啊
        //要找一个节点的random节点,得先找到random节点是第几个,然后再从新的链表上找到这两个节点在相互连接起来
        //然后优化时间复杂度,用一个哈希表把两个对应的结点存储起来,这样在找random结点时就可以很快找到
        Map<Node, Node> map = new HashMap<>();

        Node newhead = new Node(-1);
        //先构建普通链表
        Node index = head;
        Node index2 = newhead;
        while(index != null){
            Node temp = new Node(index.val);
            index2.next = temp;
            map.put(index, temp);
            index = index.next;
            index2 = index2.next;
        }
        index2.next = null;

        index = head;
        index2 = newhead.next;
        while(index != null){
            //找到random
            Node ran = index.random;
            index2.random = map.get(ran);
            index = index.next;
            index2 = index2.next;
        }
        return newhead.next;
    }
}

第二个思路,将复制的链表节点接到对应的原链表节点后面

class Solution {
    public Node copyRandomList(Node head) {
        //第二种方法,巧妙的解法
        //先复制链表,但是这次复制链表是将复制的结点直接加在对应的结点后面,这样做是为了方便找到random对应的结点
        //然后再遍历一次链表,将对应的random连接好
        //第三次遍历链表,把整个链表分开
        if(head == null)
            return null;
        Node curr = head;
        //第一步.复制链表
        while(curr != null){
            Node temp = new Node(curr.val);
            temp.next = curr.next;
            curr.next = temp;
            curr = curr.next.next;
        }
        //第二步,处理random结点
        curr = head;
        while(curr != null){
            if(curr.random != null){
                Node temp = curr.random;
                curr.next.random = curr.random.next;
            }
            curr = curr.next.next;
        }
        //第三步,分开
        curr = head;
        Node newhead = curr.next;
        Node curr2 = head.next;
        while(curr2.next != null){
            curr.next = curr.next.next;
            curr2.next = curr2.next.next;
            curr = curr.next;
            curr2 = curr2.next;
        }
        curr.next = null;   //如果不对最后这个结点进行处理,最后两个结点还是相连状态
        return newhead;

    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值