牛客网剑指offer -树(java实现)

一、JZ55 二叉树的深度(简单)

1、使用递归

public class Solution {
    public int TreeDepth(TreeNode root) {
        if(root == null) return 0;
         return Math.max(TreeDepth(root.left), TreeDepth(root.right)) + 1;       
    }
}

二、JZ77 按之字形顺序打印二叉树(中等)

  1. 使用队列,先装头结点,用一个变量记录当前层的size,一个变量记录当前层数,用于数据反转,通过对队列当前size值的一次次遍历,遍历的同时添加进每个节点的左右节点值,遍历完返回数组

public class Solution {
    public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        if(pRoot != null) q.offer(pRoot);
        int count = 1;
        while(!q.isEmpty()) {
            ArrayList<Integer> list = new ArrayList<Integer>();
            int size = q.size();
            for(int i = 0;i< size;i++) {
                TreeNode temp = q.poll();
                list.add(temp.val);
                if(temp.left != null) q.offer(temp.left);
                if(temp.right != null) q.offer(temp.right);
            }
            if(count%2==0) {
                Collections.reverse(list);
                res.add(list);
            } else {
                res.add(list);
            }
            count++;
        }
        return res;

    }

}

三、JZ54 二叉搜索树的第k个节点(中等)

1、中序遍历二叉搜索树,得到的数组是有序的,二叉搜索树的特点是左子树<根节点<右子树,然后取数组中第K个值

import java.util.*;
public class Solution {
    ArrayList<Integer> list = new ArrayList<>();
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param proot TreeNode类 
     * @param k int整型 
     * @return int整型
     */
    public int KthNode (TreeNode proot, int k) {
        if(proot == null || k == 0) return -1;
        EachTree(proot);
        if(k <= list.size()) {
            return list.get(k - 1);
        } else {
            return -1;
        }
    }

    void EachTree(TreeNode proot) {
        if(proot != null) {
            EachTree(proot.left);
            list.add(proot.val);
            EachTree(proot.right);
        }
    }
}

2、同样中序遍历二叉搜索树,用一个变量记录遍历了多少节点,第K个节点就是要的结果,同样是因为中序遍历的二叉搜索树是一个有序的结果

import java.util.*;
public class Solution {
    //记录返回的节点
    private TreeNode res = null;
    //记录中序遍历了多少个
    private int count = 0;
    public void midOrder(TreeNode root, int k){
        //当遍历到节点为空或者超过k时,返回
        if(root == null || count > k) 
            return;
        midOrder(root.left, k);
        count++;
        //只记录第k个
        if(count == k)  
            res = root;
        midOrder(root.right, k);
    }
    public int KthNode (TreeNode proot, int k) {
        midOrder(proot, k);
        if(res != null)
            return res.val;
        //二叉树为空,或是找不到
        else 
            return -1;
    }
}

四、JZ7 重建二叉树(中等)

1、因为前序遍历是根左右,中序遍历是左根右,所以前序遍历第一个是树根,中序遍历对应这个值的左边就是左子树,右边就是右子树,以此递归。

import java.util.*;
public class Solution {
    private HashMap<Integer,Integer> hashMap;
    public TreeNode reConstructBinaryTree(int [] pre,int [] vin) {
        int n = pre.length;
        hashMap = new HashMap<Integer,Integer>();
        for(int i = 0; i < n; i++) {
            hashMap.put(vin[i], i);
        }
        return buildTree(pre,vin,0,n-1,0,n-1);
    }

    private TreeNode buildTree(int [] pre,int [] vin, int preStart, int preEnd, int vinStart, int vinEnd) {
        if(preStart > preEnd) return null;
        int vinRoot = hashMap.get(pre[preStart]);
        TreeNode root = new TreeNode(pre[preStart]);
        int treeLeftSize = vinRoot - vinStart;
        root.left = buildTree(pre,vin,preStart + 1, preStart + treeLeftSize, vinStart, vinRoot - 1);
        root.right = buildTree(pre,vin, preStart + treeLeftSize + 1,preEnd,vinRoot + 1, vinEnd);
        return root;
    }
}

五、JZ26 树的子结构(中等)

1、先使用递归创建一个判断子树的方式,再使用一个递归通过根,左子树,右子树的方式一个个判断是否有对应的子树

public class Solution {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        //题目约定的空树不是任意一个树的子结构
        if (root2 == null) return false;
        if (root1 == null && root2 != null) return false;
        boolean res = false;
        if(root1.val == root2.val) {
            res = isSubTree(root1, root2);
        }
        if(!res) {
            res =  HasSubtree(root1.left, root2);
            if(!res) {
                res = HasSubtree(root1.right, root2);
            }
        }
        return res;
    }

    private boolean isSubTree(TreeNode root1,TreeNode root2) {
        if(root2 == null) return true;
        if(root1 == null && root2 != null) return false;
        if(root1.val == root2.val) {
            return isSubTree(root1.left,root2.left) && isSubTree(root1.right,root2.right);
        } else {
            return false;
        }
    }


}

六、JZ27 二叉树的镜像(简单)

1、使用递归,一直交换左右子树

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pRoot TreeNode类 
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        if(pRoot == null) return null;
        if(pRoot != null && pRoot.left == null && pRoot.right == null) return pRoot;
        TreeNode temp = pRoot.left;
        pRoot.left = pRoot.right;
        pRoot.right = temp;
        Mirror(pRoot.left);
        Mirror(pRoot.right);
        return pRoot;
    }

2、使用辅助栈,遍历树的所有节点 node ,并交换每个 node 的左 / 右子节点

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pRoot TreeNode类 
     * @return TreeNode类
     */
    public TreeNode Mirror (TreeNode pRoot) {
        // write code here
        if(pRoot == null) return null;
        // 构建辅助栈
        Stack<TreeNode> stack = new Stack<>();
        // 根节点入栈
        stack.add(pRoot);
        while(!stack.isEmpty()) {
            // 节点出栈
            TreeNode node = stack.pop();
            // 根节点的左右子树入栈
            if(node.left != null) stack.add(node.left);
            if(node.right != null) stack.add(node.right);
            // 左右子树交换
            TreeNode tmp = node.left;
            node.left = node.right;
            node.right = tmp;
        }
        return pRoot;
    }
}

七、JZ32 从上往下打印二叉树(简单)

1、类型之字形打印,但更简单些,不用记录层级反转该层的数据,同样使用辅助队列,先加入根节点,然后再遍历队列的同时,取出的数据再加入左右节点,从而实现层级遍历

public class Solution {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        ArrayList<Integer> res = new ArrayList<>();
        if(root != null) {
            q.offer(root);
        }
        while(!q.isEmpty()) {
            int size = q.size();
            for(int i =0; i< size;i++) {
                TreeNode temp = q.poll();
                res.add(temp.val); 
                if(temp.left != null) {
                    q.offer(temp.left);
                }
                if(temp.right != null) {
                    q.offer(temp.right);
                }
            }

        }
        return res;
    }
}

2、二维数组配合递归完成二叉树层序遍历

import java.util.*;
public class Solution {
    void traverse(TreeNode root, ArrayList<ArrayList<Integer> > res, int depth) {
        if(root != null){
            //新的一层
            if(res.size() < depth){  
                ArrayList<Integer> row = new ArrayList(); 
                res.add(row);
                row.add(root.val);
            //读取该层的一维数组,将元素加入末尾
            }else{ 
                ArrayList<Integer> row = res.get(depth - 1);
                row.add(root.val);
            }
        }
        else
            return;
        //递归左右时深度记得加1
        traverse(root.left, res, depth + 1); 
        traverse(root.right, res, depth + 1);
    }
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> res = new ArrayList();
        ArrayList<ArrayList<Integer> > temp = new ArrayList<ArrayList<Integer> >();
        if(root == null)
            //如果是空,则直接返回
            return res; 
        //递归层次遍历 
        traverse(root, temp, 1); 
        //送入一维数组
        for(int i = 0; i < temp.size(); i++)
            for(int j = 0; j < temp.get(i).size(); j++)
                res.add(temp.get(i).get(j));
        return res;
    }
}

八、JZ33 二叉搜索树的后序遍历序列(中等)

1、后序遍历是左右跟,所以跟在最后一个,而且根据二叉搜索树的性质左子树<跟<右子树,所以遍历数组找到左子树跟右子树再依次递归,得到最终结果。

public class Solution {
    public boolean VerifySquenceOfBST(int [] sequence) {
        if(sequence.length == 0) return false;
        return recur(sequence, 0, sequence.length - 1);
    }

    private boolean recur(int [] sequence,int start,int end) {
        if(start >= end) return true;
        int p = start;
        while(sequence[p] < sequence[end]) p++;
        int m = p;
        while(sequence[p] > sequence[end]) p++;
        return p == end && recur(sequence, start, m-1) && recur(sequence, m ,end - 1);
    }
}

九、JZ82 二叉树中和为某一值的路径(一)(简单)

1、因为路径是从跟节点开始的,使用递归,将值累加并传下去,有符合的就返回true

import java.util.*;
public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return bool布尔型
     */
     boolean res = false;
    public boolean hasPathSum (TreeNode root, int sum) {
        if(root == null) return false;
        if(root.val == sum && root.left == null && root.right == null) return true;
        if(root.left != null) {
            checkSum(root.left, root.val, sum);         
        }
        if(root.right != null) {
            checkSum(root.right, root.val, sum);         
        }
        return res;
    }

    private void checkSum(TreeNode root,int current, int sum){
        if(root != null) {
            if(root.val + current == sum) {
                res = true;
            }
            checkSum(root.left, root.val + current, sum);
            checkSum(root.right, root.val + current, sum);
        }
    }
}

十、JZ34 二叉树中和为某一值的路径(二)(中等)

1、这里求的路径可能有多条,而且该题路径定义为从树的根结点开始往下一直到叶子结点所经过的结点,这里用于递归的终止条件

import java.util.ArrayList;
public class Solution {
    ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
    ArrayList<Integer> path = new ArrayList<Integer>();
    public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int expectNumber) {
        dfs(root,expectNumber);
        return res;
    }

    private void dfs(TreeNode root,int expectNumber) {
        if(root == null) return;
        path.add(root.val);
        expectNumber -= root.val;
        if(root.left == null && root.right == null && expectNumber == 0) {
            res.add(new ArrayList(path));
        }
        dfs(root.left,expectNumber);
        dfs(root.right, expectNumber);
        path.remove(path.size() - 1);
    }
}

十一、JZ36 二叉搜索树与双向链表(中等)

1、中序递归搜索二叉树,因为搜索二叉树的性质会得到一个有序的遍历,在遍历的过程中依次调整指针使其成为双向链表

public class Solution {
    //返回的第一个指针,即为最小值,先定为null
    public TreeNode head = null;  
    //中序遍历当前值的上一位,初值为最小值,先定为null
    public TreeNode pre = null;   
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null)
            //中序递归,叶子为空则返回
            return null;     
        //首先递归到最左最小值  
        Convert(pRootOfTree.left); 
        //找到最小值,初始化head与pre
        if(pre == null){       
            head = pRootOfTree;
            pre = pRootOfTree;
        }
        //当前节点与上一节点建立连接,将pre设置为当前值
        else{       
            pre.right = pRootOfTree;
            pRootOfTree.left = pre;
            pre = pRootOfTree;
        }
        Convert(pRootOfTree.right);
        return head;
    }
}

十二、JZ79 判断是不是平衡二叉树(简单)

1、先判断根节点是否为null,是则直接返回true,递归得到左右子树的深度,比较差值的绝对值是否小于1,小于1才是平衡二叉树。

public class Solution {
    boolean res = true;
    public boolean IsBalanced_Solution(TreeNode root) {
        if(root == null) return true;
        dfs(root);
        return res;
    }

    private int dfs(TreeNode root) {
        if(root == null) return 0; 
        int l =dfs(root.left);
        int r =dfs(root.right);
        if(Math.abs(r - l) > 1) {
            res = false;
        }
        return Math.max(l, r) + 1;
    }
}

十三、JZ8 二叉树的下一个结点(中等)

1、题目找的是中序遍历的下一个结点,所以先中序遍历树并装进辅助ArrayList,在数组中取下一个值

import java.util.ArrayList;
public class Solution {
    ArrayList<TreeLinkNode> list = new ArrayList<TreeLinkNode>();
    public TreeLinkNode GetNext(TreeLinkNode pNode) {
        TreeLinkNode temp = pNode;
        while(temp.next != null) temp = temp.next;
        dfs(temp);
        for(int i = 0; i< list.size() - 1; i++) {
            if(list.get(i) == pNode) {
                return list.get(i + 1);
            }
        }
        return null;
    }

    private void dfs(TreeLinkNode root) {
        if(root != null) {
            dfs(root.left);
            list.add(root);
            dfs(root.right);
        }
    }
}

十四、 JZ28 对称的二叉树(简单)

1、递归判断左右子树对称的节点是否相等或同时为null,满足就是对称的二叉树

public class Solution {
    boolean isSymmetrical(TreeNode pRoot) {
        return dfs(pRoot, pRoot);
    }

    private boolean dfs(TreeNode root1, TreeNode root2){
        //可以两个都为空
        if(root1 == null && root2 == null) return true;
        //只有一个为空或者节点值不同,必定不对称
        if(root1 == null || root2 == null || root1.val != root2.val) return false;
         //每层对应的节点进入递归比较
        return dfs(root1.left, root2.right) && dfs(root1.right, root2.left);
    }
}

十五、JZ78 把二叉树打印成多行(中等)

1、同样通过辅助队列来实现层级遍历二叉树,同时用一个ArrayList装每一层的数据,并在最后加入到二维ArrayList中

import java.util.*; 
public class Solution {
    ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
        Queue<TreeNode> q = new LinkedList<>();
        if(pRoot != null) q.offer(pRoot);
        while(!q.isEmpty()) {
            int size = q.size();
            ArrayList<Integer> item = new ArrayList<>();
            for(int i = 0; i < size; i++) {
                TreeNode temp = q.poll();
                item.add(temp.val);
                if(temp.left != null) q.offer(temp.left);
                if(temp.right != null) q.offer(temp.right);
            }
            res.add(item);
        }
        return res;
    
    }
}

十六、JZ37 序列化二叉树(较难)

1、这里采用层序遍历序列化的方式,比较好通过辅助队列处理,序列化就是通过层序遍历转成一个string,同时注意需要用一个自建的emptyNode构建成完善二叉树,不然不好在反序列化时进行节点判断(讲得比较抽象,得自己想想如果不这样构建会不会有什么问题来对比作用)

import java.util.*;
public class Solution {
    int INF = 0x3f3f3f3f;
    TreeNode emptyNode = new TreeNode(INF);
    String Serialize(TreeNode root) {
        if(root == null) return "";
        StringBuilder sb = new StringBuilder();
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.offer(root);
        while(!q.isEmpty()) {
            TreeNode temp = q.poll();
            sb.append(temp.val + "-");
            if(!temp.equals(emptyNode)) {
                q.offer(temp.left != null? temp.left : emptyNode);
                q.offer(temp.right != null? temp.right : emptyNode);
            }
        }
        return sb.toString();
    }
    TreeNode Deserialize(String str) {
        if(str.equals("")) return null;
        String[] ss = str.split("-");
        int n = ss.length;
        TreeNode root = new TreeNode(Integer.parseInt(ss[0]));
        Queue<TreeNode> q = new LinkedList<>();
        q.offer(root);
        for(int i = 1;i < n -1;i+=2) {
            TreeNode temp = q.poll();
            int a = Integer.parseInt(ss[i]), b = Integer.parseInt(ss[i+1]);
            if(a != INF) {
                temp.left = new TreeNode(a);
                q.offer(temp.left);
            }
            if(b != INF) {
                temp.right = new TreeNode(b);
                q.offer(temp.right);
            }
        }
        return root;
       
    }
}

十七、JZ84 二叉树中和为某一值的路径(三)(中等)

1、这里不限制根节点开始的路径,故这里跟路径二不同在需要两个递归,一个递归是去找路径的,一个递归是递归树,让每个节点有机会当成根节点去寻找路径。

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param root TreeNode类 
     * @param sum int整型 
     * @return int整型
     */
    int res = 0;
    public int FindPath (TreeNode root, int sum) {
        if(root == null) return res;
        dfs(root, sum);
        FindPath(root.left, sum);
        FindPath(root.right, sum);
        return res;
    }

    private void dfs(TreeNode root, int sum) {
        if(root == null) return;
        if(sum == root.val) res++;
        dfs(root.left, sum - root.val);
        dfs(root.right, sum - root.val);
    }
}

十八、JZ86 在二叉树中找到两个节点的最近公共祖先(中等)

1、在左子树跟右子树遍历寻找节点,如果left为空,说明这两个节点在root结点的右子树上,我们只需要返回右子树查找的结果即可,如果right为空,说明这两个节点在root结点的左子树上,我们只需要返回左子树查找的结果即可,如果left和right都不为空,说明这两个节点一个在root的左子树上一个在root的右子树上,我们只需要返回cur结点即可

import java.util.*;
public class Solution {
    /**
     * 
     * @param root TreeNode类 
     * @param o1 int整型 
     * @param o2 int整型 
     * @return int整型
     */

    public int lowestCommonAncestor(TreeNode root, int o1, int o2) {
    return dfs(root, o1, o2).val;
}

    private TreeNode dfs(TreeNode root, int o1, int o2) {
        if(root == null || root.val == o1 || root.val == o2) return root;
        TreeNode left = dfs(root.left, o1, o2);
        TreeNode right = dfs(root.right, o1, o2);
        //如果left为空,说明这两个节点在root结点的右子树上,我们只需要返回右子树查找的结果即可
        if(left == null) return right;
         //同上
        if(right == null) return left;
          //如果left和right都不为空,说明这两个节点一个在root的左子树上一个在root的右子树上,
    //我们只需要返回cur结点即可。
        return root;
    }
}

十九、JZ68 二叉搜索树的最近公共祖先(简单)

1、这里可以利用二叉搜索树的性质(左子树<根<右子树)较方便找到对应的路径,将两条路径进行对比,第一个相同的节点就是最近的公共祖先

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param root TreeNode类 
     * @param p int整型 
     * @param q int整型 
     * @return int整型
     */
    public int lowestCommonAncestor (TreeNode root, int p, int q) {
        ArrayList<Integer> pathP = getPath(root, p);
        ArrayList<Integer> pathQ = getPath(root, q);
        int res = 0;
        for(int i = 0; i< Math.min(pathP.size(), pathQ.size());i++) {
            int x = pathP.get(i);
            int y = pathQ.get(i);
            if(x == y) {
                res = x;
            } else {
                break;
            }
        }
        return res;
    }

    private ArrayList<Integer> getPath(TreeNode root, int target) {
        ArrayList<Integer> path = new ArrayList<Integer>();
        TreeNode node = root;
        while(node.val != target) {
            path.add(node.val);
            if(node.val > target) {
                node = node.left;
            } else {
                node = node.right;
            }
        }
        path.add(node.val);
        return path;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值