Java数据结构之二叉树—模拟实现+OJ练习

 模拟实现:

首先创建一个树:

由于每一个TreeNode都是一个结点,所以创建为内部类;

public class BinaryTree {
    static class TreeNode{
        public char val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(char val) {
            this.val = val;
        }
    }
    public TreeNode creatTree(){
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        E.right = H;
        C.left = F;
        C.right = G;
        return A;
    }
}

不管是哪种方法遍历,可以想象在最后一个结点的时候代码会打印哪个字母,并且return后打印哪个,这样想思考的更清楚

每种方法都要画图

前序遍历:

   // 前序遍历
    void preOrder(TreeNode root){
        if (root == null){
            return;
        }
        System.out.println(root.val);
        postOrder(root.left);
        postOrder(root.right);
        return;
    }

中序遍历:

    // 中序遍历
    void inOrder(TreeNode root){
        if (root == null){
            return;
        }
        postOrder(root.left);
        System.out.println(root.val);
        postOrder(root.right);
        return;
    }

后序遍历:

    // 后序遍历
    void postOrder(TreeNode root){
        if (root == null){
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.println(root.val);
        return;
    }

二叉树的基本操作:

获取树中结点个数:

 关键在于只要不是空就++

第一种解法:

    public int nodeSize;
    int size(TreeNode root){
        if(root == null){
            return 0;
        }
        //关键:只要不是空就++
        nodeSize++;
        size(root.left);
        size(root.right);

        return nodeSize;
    }

 子问题解法:

树的结点个数 = 左子树的结点个数 + 右子树的结点个数 + 1;

当拿只有三个结点的二叉树去理解很清楚,当结点很多,同样适用:

  int size(TreeNode root){
        if(root == null){
            return 0;
        }
        //子问题
        return  size(root.left)+
        size(root.right)+1;
    }

 获取叶子结点的个数:

遍历思路:

 按照某种遍历方式(四种遍历方法),遍历到某个结点判断式是叶子结点,计数器++

  public int leafNodeCount;
    int getLeafNodeCount(TreeNode root){
        if(root == null){
            return 0;
        }
        if(root.left == null&&root.right == null){
            leafNodeCount++;
        }
        getLeafNodeCount(root.left);

        getLeafNodeCount(root.right);

        return leafNodeCount;

    }

 子问题解法:

叶子个数 = 左子树子叶个数 + 右子树子叶个数

  int getLeafNodeCount(TreeNode root){
        if(root == null){
            return 0;
        }
        if(root.left == null&&root.right == null){
            return 1;
        }
        return getLeafNodeCount(root.left)+
                getLeafNodeCount(root.right);
    }

获取第K层结点的个数:

子问题方法:

整棵树的第K层节点个数 = 左子树第 K -1 层结点个数 + 右子树第 K - 1 层结点个数 



   int getKLevelNodeCount(TreeNode root,int k){
        if(root == null){
            return 0;
        }
        if(k == 1){
            return 1;
        }
        return getKLevelNodeCount(root.left,k-1)
                + getKLevelNodeCount(root.right, k-1);
    }

 获取二叉树的高度:

 二叉树的高度 = 左子树的高度 和 右子树高度 的最大的值 + 1;

加的1 是根结点,对于叶子结点,左右树都是0 返回的就是自己单独一个结点的高度 

  // 获取二叉树的高度
    int getHeight(TreeNode root){
        if(root == null){
            return 0;
        }
        int leftTree = getHeight(root.left);
        int rightTree = getHeight(root.right);
        return leftTree > rightTree ?
                leftTree + 1 : rightTree + 1;
    }

 检测值为value的值是否存在:

 思路:

1.先判断是否为null

2.判断根结点的val是否等于 val

3.遍历左子树

4.遍历右子树

从3 .4 开始递归

   // 检测值为value的元素是否存在
    TreeNode find(TreeNode root, int val){
        if(root == null){
            return null;
        }
        if(root.val == val){
            return root;
        }
        TreeNode ret1 = find(root.left,val);
        if (ret1 != null){
            return ret1;
        }
        TreeNode ret2 = find(root.right,val);
        if(ret2 != null){
            return ret2;
        }
        return null;

    }

层序遍历:

使用队列去实现:

   void levelOrder(TreeNode root){
        if(root == null){
            return ;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
           TreeNode cur = queue.poll();
            System.out.print(cur.val+" ");
           if(cur.left != null){
               queue.offer(cur.left);
           }
           if(cur.right != null){
               queue.offer(cur.right);
            }
        }

    }

 判断一棵树是不是完全二叉树:

思路:

    boolean isCompleteTree(TreeNode root){
        if(root == null){
            return true;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            TreeNode cur = queue.poll();
            if(cur != null){
               queue.offer(cur.left);
               queue.offer(cur.right);
            }else {
                break;
            }
        }
//出现空 判断队列中是否还有非空
        while(!queue.isEmpty()){
            TreeNode tmp = queue.peek();
            if(tmp == null){
                queue.poll();
            }else {
                return false;
            }
        }
        return true;
    }

LeetCode题目:

144.二叉树的前序遍历

遍历思维做法:

  List<Integer> List = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {

            if (root == null){
                return List;
            }
        List.add(root.val);
        preorderTraversal(root.left);
        preorderTraversal(root.right);
            return List;

    }

利用好返回值:

      public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
       if (root == null){
           return list;
       }
       list.add(root.val);

       List<Integer> leftTree = preorderTraversal(root.left);
      list.addAll(leftTree);
       List<Integer> rightTree = preorderTraversal(root.right);
       list.addAll(rightTree);
       return list;

   }

 非递归:

void preorderTraversal(TreeNode root) {
        if(root == null){
            return ;
        }
        Stack<TreeNode> stack = new Stack();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                System.out.print(cur.val+ " ");
                cur = cur.left;

            }
            TreeNode top  = stack.pop();
            cur = top.right;
        }

    }
}

94. 二叉树的中序遍历

非递归打印:

思路:

  void inorderTraversal(TreeNode root) {
        if(root == null){
            return;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            System.out.print(top.val + " ");
            cur = top.right;
        }
    }

145. 二叉树的后序遍历

 博哥代码:

    void postorderTraversal(TreeNode root) {
        if(root == null){
            return;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            if(top.right == null || top.right == prev) {
                System.out.print(top.val+" ");
                stack.pop();
                prev = top;
            }else {
                cur = top.right;
            }
        }

    }

我的思路: 

    void postorderTraversal2(TreeNode root) {
    if(root == null){
        return;
    }
    Stack<TreeNode> stack1 = new Stack<>();
    Stack<TreeNode> stack2= new Stack<>();
    TreeNode cur = root;
    while(cur != null || !stack1.isEmpty()){
        while(cur != null){
            stack1.push(cur);
            cur = cur.left;
        }
        TreeNode top = stack1.peek();
        stack2.push(top);
        cur = top.right;
        if(cur == null){
            while(!stack1.isEmpty()&&!stack2.isEmpty()&&
                    stack2.peek()==stack1.peek() ){
                System.out.print(stack1.peek().val+" ");
                stack1.pop();
                stack2.pop();
            }
            if(stack1.isEmpty())
            {
                break;
            }
                top = stack1.peek();
                stack2.push(top);
                cur = top.right;
        }
    }
    }

 100. 相同的树

思路: 

  public boolean isSameTree(TreeNode p, TreeNode q) {
//先从结构上,再从数值上

//结构上
//一个为空一个不为空 肯定不相同
        if (p == null && q != null || p != null && q == null){
            return false;
        }
//两个都为空
        if(p == null && q == null){
            return true
        }
//数值上
//值不相等 不相同{根节点的判断}
        if(p.val != q.val){
            return false;
        }
//左右树的值进行以上 结构 和 值上的判断 
        return isSameTree(p.left,q.left)&&
        isSameTree(p.right,q.right);

    }

心得:

在二叉树的操作或者题目上,都是针对结点进行各种情况的判断(一般是先判断结构 再 判断数值),然后再递归,这样递归到任何结点,进行的还是以上红色字体的操作

本题再return之前的操作都针对具体结点的分析 先结构 再 数值 然后递归 重复以上循环

572. 另一棵树的子树

正确代码:

错误代码,还没有解决

 public boolean isSameTree(TreeNode p, TreeNode q) {
        if (p == null && q != null || p != null && q == null){
            return false;
        }
        if(p == null && q == null){
            return true;
        }
        if(p.val != q.val){
            return false;
        }
        return isSameTree(p.left,q.left)&&
        isSameTree(p.right,q.right);

    }
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(root == null || subRoot == null){
            return false;
        }
      if(isSameTree(root,subRoot)){
          return true;
      }

      return isSameTree(root.left,subRoot) ||
        isSameTree(root.right,subRoot);

    }

 226. 翻转二叉树

思路:

抽象到只有三个节点的满二叉树,那么只需要根节点的左右子树进行交换就可以实现

所以具体到结点,交换根结点的左右子树,抽象到整体使用前序遍历对所有的结点进行上面红色的操作

 其中,判断结点左右子树是否都为空,这样最起码会减少一般的遍历次数

public TreeNode invertTree(TreeNode root) {
        if(root == null){
            return null;
        }
        //如果左右子树都为空就不再交换
        if (root.left == null && root.right == null){
            return root;
        }
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }

110. 平衡二叉树

子问题:

当左右子树都为平衡二叉树,整棵树才是平衡二叉树 

 public boolean isBalanced(TreeNode root) {

        if(root == null){
            return true;
        }
            return getHeight(root) >= 0;
    }
      int getHeight(TreeNode root){
        if(root == null){
            return 0;
        }
        int leftTree = getHeight(root.left);
        if(leftTree < 0){
            return -1;
        }
        int rightTree = getHeight(root.right);
       if(leftTree >= 0 && rightTree >= 0 && Math.abs(leftTree - rightTree) <= 1){
        return Math.max(leftTree,rightTree)+1;
       }else{
        return -1;
       }
    }

时间复杂度O(N); 

101. 对称二叉树

思路:

关键:判断2.3同时为真时才是对称的 

    public boolean isSymmetric(TreeNode root) {

if(root == null){
    return true;
}
return isSymmetricChild(root.left,root.right);
    }
    private boolean isSymmetricChild(TreeNode leftTree,TreeNode rightTree) {
        if(leftTree == null && rightTree != null || leftTree != null && rightTree == null){
            return false;
        }
        if(leftTree == null && rightTree == null){
            return true;
        }
       
            if(leftTree.val != rightTree.val){
                return false;
            }
            return isSymmetricChild(leftTree.left,rightTree.right)&&isSymmetricChild(leftTree.right,rightTree.left);
        
    }

注意:

两个值相同时,return ture ; 局部的正确, 当该结点下面的不相同时就是错的 

 

KY11 二叉树遍历

 注意:

i被static修饰的原因:

class TreeNode{
    public char val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(char val){
        this.val = val;
    }
}
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static int i;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
            String str = in.nextLine();
            TreeNode root = creatTree(str);
            inorder(root);
        }
    }
    public static TreeNode creatTree(String str){
            TreeNode root = null;
            if(str.charAt(i) != '#'){
                root = new TreeNode(str.charAt(i));
                i++;
                root.left = creatTree(str);
                root.right = creatTree(str);
            }else{
                i++;
            }
            return root;
      }  
    public static void inorder(TreeNode root){
        if(root == null){
        return ;
         }
        inorder(root.left);
         System.out.print(root.val+" ");
        inorder(root.right);
}


}

102. 二叉树的层序遍历

思路:

  public List<List<Integer>> levelOrder(TreeNode root) {
        //创建个二维数组
        List<List<Integer>> ret = new ArrayList<>();
        if(root == null){
            return ret;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            //每一层数值 封装在一个新的tmp数组内
            List<Integer> tmp = new ArrayList<>();
            int size = queue.size();
            while(size != 0) {
                TreeNode cur = queue.poll();
                //将本层的数值 添加到tmp中去
                tmp.add(cur.val);
                size--;
                System.out.print(cur.val + " ");
                if (cur.left != null) {
                    queue.offer(cur.left);
                }
                if (cur.right != null) {
                    queue.offer(cur.right);
                }
            }
            ret.add(tmp);
        }
        return ret;

    }

236. 二叉树的最近公共祖先

任何叶子结点的.left .right 都是 null ,当遍历到时 返回 null , 像leftTree 接收的 会有 null 的情形

使用递归:

思路:

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null){
            return null;
        }
        if(root == q || root == p){
            return root;
        }
        TreeNode leftTree = lowestCommonAncestor(root.left,p,q);
        TreeNode rightTree = lowestCommonAncestor(root.right,p,q);
        if(leftTree != null && rightTree != null){
            return root;
        }else if(leftTree != null){
            return leftTree;
        }else{
            return rightTree;
        }
    }

使用栈:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
      if(root == null){
        return null;
      }
      Stack<TreeNode> stackP = new Stack<>();
      Stack<TreeNode> stackQ = new Stack<>();
      getPath(root,p,stackP);
      getPath(root,q,stackQ);
    int sizeP = stackP.size();
    int sizeQ = stackQ.size();
    if(sizeP > sizeQ){
        int size = sizeP - sizeQ;
        while(size != 0){
            stackP.pop();
            size--;
        }
    }else{
        int size = sizeQ - sizeP;
        while(size != 0){
            stackQ.pop();
            size--;
        }
    }
    while(!stackP.isEmpty() && !stackQ.isEmpty()){
        if(stackP.peek() == stackQ.peek()){
            return stackP.peek();
        }else{
            stackP.pop();
            stackQ.pop();
        }
    }
    return null;

    }
    public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode>stack){
        if(root == null){
            return false;
        }
        stack.push(root);
        if(root == node){
            return true;
        }
        boolean leftTree = getPath(root.left,node,stack);
        if(leftTree == true){
            return true;
        }
        boolean rightTree = getPath(root.right,node,stack);
        if(rightTree == true){
            return true;
        }
        stack.pop();
        return false;
    }
}

105. 从前序与中序遍历序列构造二叉树

思路:

先序遍历找根节点,中序遍历找根节点左子树和右子树 

class Solution {
    public int preIndex;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTreeChilde(preorder,inorder,0,inorder.length - 1);
    }
    public TreeNode buildTreeChilde(int[] preorder, int[] inorder,int inbegin,int inend){
        if(inbegin > inend){
            return null;
        }
        TreeNode root = new TreeNode(preorder[preIndex]);
       
        int rootIndex = findRootIndex(inorder,inbegin,inend,preorder[preIndex]);
         //找到root的下标
        if(rootIndex == -1){
            return null;
        }
        preIndex++;
        root.left =buildTreeChilde(preorder,inorder,inbegin,rootIndex-1);
        root.right =buildTreeChilde(preorder,inorder,rootIndex + 1,inend);
        return root;
    }
    public int findRootIndex(int[] inorder,int inbegin,int inend,int key){
        for(int i = inbegin; i <= inend; i++){
            if(inorder[i] == key){
                return i;
            }
        }
        return -1;
    }
}

 106. 从中序与后序遍历序列构造二叉树

后序遍历找根结点,创建二叉树的时候先创建右树,因为后序遍历时左右根,倒着遍历时先遍历到右根 

class Solution {
    public int postIndex;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        postIndex = postorder.length - 1;
        return buildTreeChilde(postorder,inorder,0,inorder.length - 1);
    }
       public TreeNode buildTreeChilde(int[] postorder, int[] inorder,int inbegin,int inend){
        if(inbegin > inend){
            return null;
        }
        TreeNode root = new TreeNode(postorder[postIndex]);
       
        int rootIndex = findRootIndex(inorder,inbegin,inend,postorder[postIndex]);
         //找到root的下标
        if(rootIndex == -1){
            return null;
        }
        postIndex--;
        root.right =buildTreeChilde(postorder,inorder,rootIndex + 1,inend);
        root.left =buildTreeChilde(postorder,inorder,inbegin,rootIndex-1);
        return root;
    }
    public int findRootIndex(int[] inorder,int inbegin,int inend,int key){
        for(int i = inbegin; i <= inend; i++){
            if(inorder[i] == key){
                return i;
            }
        }
        return -1;
    }
}

606. 根据二叉树创建字符串

思路: 

理解一下,当右边为空,回溯后在上次层的递归添加小括号

class Solution {
    public String tree2str(TreeNode root) {
    StringBuilder stringbuilder = new StringBuilder();
    tree2strChiled(root,stringbuilder);    
    return stringbuilder.toString();
    }
     public void tree2strChiled(TreeNode t,StringBuilder stringbuilder) {
        if(t == null){
            return;
        }
        stringbuilder.append(t.val);
        if(t.left != null){
            stringbuilder.append("(");
            tree2strChiled(t.left,stringbuilder);
            stringbuilder.append(")");
        }else{
            //右边为空
            if(t.right == null){
               return; 
            }else{
                //右边不为空
                stringbuilder.append("()");
            }

        }
        if(t.right != null){
            stringbuilder.append("(");
            tree2strChiled(t.right,stringbuilder);
            stringbuilder.append(")");
        }else{
            return;
        }

    }
}

  • 22
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
二叉树是一种常见的数据结构,它由节点组成,每个节点最多有两个子节点:左子节点和右子节点。以下是一个简单的Java实现二叉树的例子: ```java // 定义二叉树节点类 class TreeNode { int val; TreeNode left; TreeNode right; public TreeNode(int val) { this.val = val; this.left = null; this.right = null; } } // 构建二叉树 public class BinaryTree { TreeNode root; public BinaryTree(int val) { this.root = new TreeNode(val); } // 插入节点 public void insert(int val) { TreeNode newNode = new TreeNode(val); if (root == null) { root = newNode; } else { TreeNode current = root; TreeNode parent; while (true) { parent = current; if (val < current.val) { current = current.left; if (current == null) { parent.left = newNode; return; } } else { current = current.right; if (current == null) { parent.right = newNode; return; } } } } } // 先序遍历 public void preOrderTraversal(TreeNode node) { if (node != null) { System.out.print(node.val + " "); preOrderTraversal(node.left); preOrderTraversal(node.right); } } // 中序遍历 public void inOrderTraversal(TreeNode node) { if (node != null) { inOrderTraversal(node.left); System.out.print(node.val + " "); inOrderTraversal(node.right); } } // 后序遍历 public void postOrderTraversal(TreeNode node) { if (node != null) { postOrderTraversal(node.left); postOrderTraversal(node.right); System.out.print(node.val + " "); } } } // 创建二叉树并进行遍历 public class Main { public static void main(String[] args) { BinaryTree binaryTree = new BinaryTree(5); binaryTree.insert(3); binaryTree.insert(7); binaryTree.insert(2); binaryTree.insert(4); binaryTree.insert(6); binaryTree.insert(8); System.out.println("先序遍历结果:"); binaryTree.preOrderTraversal(binaryTree.root); System.out.println(); System.out.println("中序遍历结果:"); binaryTree.inOrderTraversal(binaryTree.root); System.out.println(); System.out.println("后序遍历结果:"); binaryTree.postOrderTraversal(binaryTree.root); System.out.println(); } } ``` 这个例子展示了如何构建一个二叉树,并对其进行先序、中序和后序遍历。你可以根据需要修改节点的值和插入顺序来构建不同的二叉树

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值