Leetcode笔记——树

这篇博客汇总了关于二叉树的各种操作,包括在二叉搜索树中找到第k大节点、查找最近公共祖先、翻转二叉树、合并二叉树、前序和层序遍历,以及二叉搜索树与双向链表的转换。此外,还介绍了路径问题,如求和为特定值的路径以及根到叶子节点的数字之和。最后探讨了如何验证一个数组是否为二叉搜索树的后序遍历结果。
摘要由CSDN通过智能技术生成

树:

1. 二叉搜索树的第k大节点

2. 公共祖先

  • 二叉树的公共祖先
  • 二叉搜索树的公共祖先

3. 翻转二叉树

4. 合并二叉树

5. 树的遍历

  • 前序遍历
  • 层序遍历

6. 二叉搜索树与双向链表

7. 路径问题

  • 二叉树中和为某一值的路径
  • 求根到叶子节点数字之和

8. 根据数组判断是否为指定二叉树

  • 判断数组是否是 二叉搜索树的后序遍历

1. 二叉搜索树的第k大节点

class Solution {

    int ans = 0;
    int find = -999;

    public int kthLargest(TreeNode root, int k) {
        dfs(root, k);
        return find;
    }

    public void dfs(TreeNode root, int k){
        if(find != -999)    return;
        if(root != null){
            dfs(root.right, k);
            ans++;
            if(ans == k){
                find = root.val;
                return;
            }
            dfs(root.left, k);
        }

    }
}

2.1 二叉树的最近公共祖先

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null; // 如果树为空,直接返回null
        if(root == p || root == q) return root; // 如果 p和q中有等于 root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先)
        TreeNode left = lowestCommonAncestor(root.left, p, q); // 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁
        TreeNode right = lowestCommonAncestor(root.right, p, q); // 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁
        if(left == null) return right; // 如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else if(right == null) return left; // 否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先)
        else return root; //否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root
    }
}

2.2 二叉搜索树的最近公共祖先

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null)    return null;
        int val = root.val;
        if(val > p.val && val < q.val){
            return root;
        }
        else if(val < p.val && val < q.val){
            return lowestCommonAncestor(root.right, p, q);
        }
        else if(val > p.val && val > q.val){
            return lowestCommonAncestor(root.left, p, q);
        }
        else{
            return root;
        }
    }   
}

3. 翻转二叉树

     4                       4
   /   \                   /   \ 
  2     7        ->       7     2   
 / \   / \               / \   / \
1   3 6   9             9   6 3   1
-----------------------------------------------
class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null)    return root;
        TreeNode temp = root.left;
        root.left = invertTree(root.right);
        root.right = invertTree(temp);
        return root;
    }
}

4. 合并二叉树

输入: 
	Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
输出: 
合并后的树:
	     3
	    / \
	   4   5
	  / \   \ 
	 5   4   7
-------------------------------------------------------
class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if(t1 == null && t2 == null)     return null;
        if(t1 == null && t2 != null)     return t2;
        if(t1 != null && t2 == null)     return t1;
        t1.val = t1.val + t2.val;
        t1.left = mergeTrees(t1.left, t2.left);
        t1.right = mergeTrees(t1.right, t2.right);
        return t1;
    }
}

5.1 树的前序遍历

非递归前序遍历
-------------------------------------------------------
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        if(root == null)    return list;
        Stack<TreeNode> stack = new Stack<>();
        while(root != null || stack.size() != 0){
            while(root != null){
                list.add(root.val);
                stack.push(root);
                root = root.left;
            }
            TreeNode temp = stack.pop();
            root = temp.right;
        }
        return list;
    }
}

5.2 层序遍历

基于栈的层序遍历
---------------------------------
class Solution {
    public int[] levelOrder(TreeNode root) {
        if(root == null)    return new int[0];
        Queue<TreeNode> queue = new LinkedList<>();
        int[] ans = new int[2000];
        int j = 0;
        queue.offer(root);
        while(queue.size() != 0){
            int size = queue.size();
            for(int i =0; i< size; i++){
                TreeNode temp = queue.poll();
                ans[j++] = temp.val;
                if(temp.left != null)   queue.offer(temp.left);
                if(temp.right != null)   queue.offer(temp.right);
            }
        }
        return Arrays.copyOfRange(ans,0,j);
    }
}

6. 二叉搜索树与双向链表

我们希望将这个二叉搜索树转化为双向循环链表。链表中的每个节点都有一个前驱和后继指针。对于双向循环链表,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点。

上图展示了上面的二叉搜索树转化成的链表。“head” 表示指向链表中有最小元素的节点。
特别地,我们希望可以就地完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中的第一个节点的指针。
-------------------------------------------------------------------------------
思路:本质是前序遍历,用pre记录前一个节点

class Solution {

    Node pre,head;    //

    public Node treeToDoublyList(Node root) {
        if(root == null)    return null;
        dfs(root);
        head.left = pre;    //此时已经初始化了,head已经成为前序第一个节点
        pre.right = head;    //pre已经成为前序最后一个节点
        return head;
    }

    public void dfs(Node root){
        if(root == null)    return;
        dfs(root.left);
        if(pre == null)     head = root;    //pre==null说明还未初始化,这是前序第一个节点
        else    pre.right = root;    //已经初始化了,让前一个节点的right等于现在的root
        root.left = pre;
        pre = root;
        dfs(root.right);
    }

}

7.1 二叉树中和为某一值的路径

给定如下二叉树,以及目标和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
------------------------------------------------------
返回:
[
   [5,4,11,2],
   [5,8,4,5]
]
---------------------------------------------------------------------
思路: 回溯
class Solution {

    List<List<Integer>> ans;

    public List<List<Integer>> pathSum(TreeNode root, int sum) {
        ans = new ArrayList<>();
        if(root == null)    return ans;
        List<Integer> list = new ArrayList<>();
        dfs(root, list, 0, sum);
        return ans;
    }

    public void dfs(TreeNode root, List<Integer> list, int temp, int sum){
        if(root == null)    return;
        if(root.left == null && root.right == null && temp+root.val == sum){
            list.add(root.val);
            ans.add(new ArrayList<Integer>(list));
        }
        else{
            list.add(root.val);
            dfs(root.left, list, temp + root.val, sum);
            dfs(root.right, list, temp + root.val, sum);
        }
        list.remove(list.size() - 1);
    }
        
}

7.2 求根到叶子节点数字之和

输入: [1,2,3]
    1
   / \
  2   3
输出: 25
解释:
从根到叶子节点路径 1->2 代表数字 12.
从根到叶子节点路径 1->3 代表数字 13.
因此,数字总和 = 12 + 13 = 25.
--------------------------------------------------
class Solution {

    int result = 0;

    public int sumNumbers(TreeNode root) {
        dfs(root, 0);
        return result;
    }

    public void dfs(TreeNode root, int presum){
        if(root == null)    return;
        int sum = presum*10 + root.val;
        if(root.left == null && root.right == null)     result += sum;
        else{
            dfs(root.left, sum);
            dfs(root.right, sum);
        } 
        return;
    }
}

8.1 判断数组是否是 二叉搜索树的后序遍历

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

输入: [1,6,3,2,5]
输出: false //不是二叉搜索树的后序遍历

输入: [1,3,2,6,5]
输出: true //是的
     5
    / \
   2   6
  / \
 1   3
-----------------------------------------------------
思路:左右指针
当数组长度<=2,一定是
不然每次取数组最后一个数,这个数一定是root
left从左开始扫描,right从右开始扫描
(1)left扫描到了结尾,说明全部都是左子树 / right扫描到了开头,全部都是右子树
(2)left -1 = right,说明满足 左边都小,右边都大的趋势,递归 左右
(3)其他情况,返回false
class Solution {
    public boolean verifyPostorder(int[] postorder) {
        if(postorder.length <= 2)   return true;
        int len = postorder.length;
        int root = postorder[len-1];
                //System.out.println(root);
        
        int left = 0;
        int right = len-2;
        while(left <= len-2 && postorder[left] < root){
            left++;
        }
        while(right >= 0 && postorder[right] > root){
            right--;
        }
        if(left == len-1 || right == -1)    return verifyPostorder(Arrays.copyOfRange(postorder,0,len-1));
        else if(left - 1 == right)   return verifyPostorder(Arrays.copyOfRange(postorder,0,left)) && verifyPostorder(Arrays.copyOfRange(postorder,left,len-1));
        else    return false;
        
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值