算法-----树(三)

填充每个节点的下一个右侧节点指针 II

 

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。

进阶:

  • 你只能使用常量级额外空间。
  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

示例:

 

输入:root = [1,2,3,4,5,null,7]
输出:[1,#,2,3,#,4,5,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化输出按层序遍历顺序(由 next 指针连接),'#' 表示每层的末尾。

题解:

这道题目太有意思了,看了答案才才明白,原来二叉树还能这样遍历,指针玩出了很多花样

1、每层新创建dummyNode作为哨兵节点,指针赋值给临时Node  pre,通过pre指针串起每一行的节点

2、root节点首先作为cur,辅助完成pre完成左右子树的串联,然后用cur=dummyNode.next完成切换到第二行的最左节点

3、第二行已经完成串联,通过cur的next右移,辅助完成其衍生左右子树的pre串联

class Solution {
    public Node connect(Node root) {
        if(root==null){
            return root;
        }
        Node cur=root;
        while(cur!=null){
            //每层创建哨兵节点
            Node dummyNode=new Node(0);
            //临时节点串联每一行节点
            Node pre =dummyNode;
            while(cur!=null){
                if(cur.left!=null){
                    pre.next=cur.left;
                    pre=pre.next;
                }

                if(cur.right!=null){
                    pre.next=cur.right;
                    pre=pre.next;
                }
                //cur当前行的节点已经被上一轮pre串联完毕,此处切换到同行右树节点
                cur=cur.next;
            }
            //关键点,cur切换到下一行
            cur=dummyNode.next;
        }

        return root;
    }
}

 

最长同值路径

 

给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。

注意:两个节点之间的路径长度由它们之间的边数表示。

示例 1:

输入:

              5
             / \
            4   5
           / \   \
          1   1   5
输出:

2
示例 2:

输入:

              1
             / \
            4   5
           / \   \
          4   4   5
输出:

2
注意: 给定的二叉树不超过10000个结点。 树的高度不超过1000。

题解:

又是一道看了半天答案才看懂的题目,不是从根节点向下的路径问题(任意节点到任意节点的路径),一般解法是找出其中根节点求出左侧路径left和右侧路径right   然后相加即可

这道题目的难点在于:当前节点和左右子树节点相同的时候路径计算需要当前节点加上左右子树,同时当前节点如果和父节点值也相同计算路径需要当前节点和其中一个子节点来计算

题解选择后续遍历  首先当前节点的左右子节点的同值路径left和right,left+right获得当前节点为根节点是的path,当前节点非根节点时Math.max(ans,arrowLeft+arrowRight)得到当前节点下的同值路径,还有一个难理解的点是使用了arrowLeft和arrowRight

而不是直接用left和right自加,原因是如果当前节点(以当前节点是父节点左子树为例)和父节点的值不同的话,父节点取path应该是arrowLeft=0不应该是已经赋值的left

class Solution {
    int ans;
    public int longestUnivaluePath(TreeNode root) {
        ans=0;
        dfs(root);
        return ans;
    }
    public int dfs(TreeNode root){
        if(root==null){
            return 0;
        }
        int arrowLeft=0,arrowRight=0;
        int left=dfs(root.left);
        int right=dfs(root.right);

        if(root.left!=null && root.left.val==root.val){
            arrowLeft+=left+1;
        }
        if(root.right!=null && root.right.val==root.val){
            arrowRight+=right+1;
        }

        ans=Math.max(ans,arrowLeft+arrowRight);
        return Math.max(arrowLeft,arrowRight);

    }
}

863. 二叉树中所有距离为 K 的结点

示例 1:

输入:root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2
输出:[7,4,1]
解释:
所求结点为与目标结点(值为 5)距离为 2 的结点,
值分别为 7,4,以及 1

注意,输入的 "root" 和 "target" 实际上是树上的结点。
上面的输入仅仅是对这些对象进行了序列化描述。
 

提示:

给定的树是非空的。
树上的每个结点都具有唯一的值 0 <= node.val <= 500 。
目标结点 target 是树上的结点。
0 <= K <= 1000.

题解:这道题目的关键点:

1、使用Map记录当前节点和父节点的对应关系,这个使用很常见

2、使用Deque链表BFS遍历记录当前节点的左右子节点和父节点,关键技巧:链表头放置一个null节点和使用hashset过滤重复节点。然后每次poll()出来null节点就增加一次计数,代表距离+1

null节点和hashset的配合使用很巧妙,达到了已节点为中心,圆形向外扩散遍历的效果

class Solution {
    Map<TreeNode,TreeNode>map=new HashMap();
    Deque<TreeNode>deque=new LinkedList();
    Set<TreeNode>set=new HashSet();
    public List<Integer> distanceK(TreeNode root, TreeNode target, int k) {
        if(root==null){
            return list;
        }
        dfs(root,null);
        deque.add(null);
        deque.add(target);
        set.add(target);
        set.add(null);
        int count=0;
        while(!deque.isEmpty()){
            TreeNode node=deque.poll();
            if(node==null){
                if(count==k){
                    List<Integer>list=new ArrayList();
                    for(TreeNode cur:deque){
                        list.add(cur.val);
                    }
                    return list;
                }
                deque.add(null);
                count++;
            }else{
                if(!set.contains(node.left)){
                    deque.add(node.left);
                    set.add(node.left);
                }
                 if(!set.contains(node.right)){
                    deque.add(node.right);
                    set.add(node.right);
                }
                TreeNode par=map.get(node);
                 if(!set.contains(par)){
                    deque.add(par);
                    set.add(par);
                }
            }
        }
        return list;
    }

    public void dfs(TreeNode node,TreeNode par){
        if(node==null){
            return;
        }
        map.put(node,par);
        dfs(node.left,node);
        dfs(node.right,node);
    }
}

652. 寻找重复的子树

给定一棵二叉树,返回所有重复的子树。对于同一类的重复子树,你只需要返回其中任意一棵的根结点即可。

两棵树重复是指它们具有相同的结构以及相同的结点值。

示例 1:

        1
       / \
      2   3
     /   / \
    4   2   4
       /
      4
下面是两个重复的子树:

      2
     /
    4

    4
因此,你需要以列表的形式返回上述重复子树的根结点。

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

 

题解:这题用到的知识点是二叉树的序列化,时间复杂度的优化关键点是把序列化的子串使用uid代替,减少子串拼接的耗时,使用stringBuilder拼接子串

class Solution {
    int t;
    Map<String,Integer>trees;
    Map<Integer,Integer>count;
    List<TreeNode>list;
    public List<TreeNode> findDuplicateSubtrees(TreeNode root) {
        t=1;
        trees=new HashMap();
        count=new HashMap();
        list=new ArrayList();
        if(root==null){
            return list;
        }

        dfs(root);

        return list;
    }

    public Integer dfs(TreeNode node){
        if(node==null){
            return 0;
        }
         StringBuilder sb=new StringBuilder();
         sb.append(node.val);
         sb.append(dfs(node.left));
         sb.append(dfs(node.right));
         String s=sb.toString();
         int uid=trees.computeIfAbsent(s,x->t++);
         count.put(uid,count.getOrDefault(uid,0)+1);
         if(count.get(uid)==2){
             list.add(node);
         }
         return uid;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值