二叉树

个人LeetCode刷题总结。
1.深度优先遍历
2..分层遍历二叉树或广度优先遍历
3.求二叉树中节点的个数
4.求二叉树的深度
5.将二叉查找树变为有序的双向链表
6.求二叉树第K层的节点个数
7.求二叉树叶子节点的个数
8.判断两颗二叉树是否同构
9.判断二叉树是不是平衡二叉树(AVL树)
10.求二叉树的镜像
11.求二叉树中节点的最大距离
12.由中序遍历和后序遍历重建二叉树
13.判断一棵树是不是完全二叉树

1.深度优先遍历

深度优先遍历分为前序遍历,中序遍历,后序遍历。

1.前序遍历(先访问根节点,再访问左子树,最后访问右子树)

1.前序遍历递归解法:

1.先判断根节点是否是否为空,为空直接返回
2.如果不为空,先访问根节点的值,再访问左子树,最后访问右子树

     public void preOrderedTraverse(Node<T> node){
               if(node == null){
                    return;
               }
               System.out.println(node.element);//这里只是简单地打印一下值
               preOrderedTraverse(node.left);
               preOrderedTraverse(node.right);
               }
2.前序遍历非递归解法:
       /**
           * 先序遍历的非递归解法:
           * 总的就是利用栈结构先进后出特点
           * 1.判断根节点是否为空,为空直接返回
           * 2.初始化之后将根节点加入栈
           * 3.只要栈不为空就进入循环,先poll出栈顶节点并访问,然后先push进右节点(如果不为空),
           * 在push左节点(如果不为空的话) 
           * @param node
           */

          public void preOrderTraverseByStack(Node<T> root){
               if(root == null){
                    return;
               }
               Stack<Node<T>> stack = new Stack<>();
               stack.push(root);
               while(!stack.isEmpty()){
                    Node<T> cur = stack.pop();
                    System.out.println(cur.element);//这里访问就是简单的打印一下
                    if(cur.right != null){
                         stack.push(cur.right);
                    }
                    if(cur.left != null){
                         stack.push(cur.left);
                    }
               }
          }

2.中序遍历(先访问左子树,再访问根节点,最后访问右子树)

      1.中序遍历递归解法
           1.先判断根节点是否是否为空,为空直接返回
           2.如果不为空,先访问左子树,再访问根节点的值,最后访问右子树
           public void midOrderedTraverse(Node<T> node){
                if(node == null){
                     return;
                }
                midOrderedTraverse(node.left);
                System.out.println(node.element);
                midOrderedTraverse(node.right);
           }
      2.中序遍历非递归解法
           /**
            * 中序遍历非递归解法
            * 使用栈结构先进后出,后进先出的特点
            * 1.如果根节点为空,就返回
            * 2.创建一个指针节点cur,一开始指向root
            * 
            * 3.只要栈不为空或者指针节点cur就进入栈
            * 4.寻找当前节点cur做为根节点的最左节点,并把一路上遇到的左节点都push入栈
            * 5.通过步骤4,将最左节点放到了栈顶,poll并访问这个节点
            * 6.访问最左节点的右子树,即将cur节点重新指向为步骤5返回节点的右节点,同样的也要将右子树上的根节点和左节点们加入栈,如果他们存在的话
            * 
            * 总的来说,就是将从根节点开始陆续把左节点们放到队列中,然后访问第一个左节点,然后访问该节点的右子树,并把该节点及左节点们加入栈
            * @param root
            */
           public void midOrderTraveseByStack(Node<T> root){
                if(root == null){
                     return;
                }
                Node<T> cur = root;
                Stack<Node<T>> stack = new Stack<>();
                while(cur!= null || !stack.isEmpty()){
                     while(cur != null){//把当前节点和一路上的左节点们加入队列
                          stack.push(cur);
                          cur = cur.left;
                     }
                     Node<T> node = stack.pop();//返回当前最左节点
                     System.out.println(node.element);//这里就用打印表示访问的逻辑了。。
                     cur = node.right; //访问当前最左节点的右子树
                }
           }

3.后序遍历(先访问左子树,再访问右子树,最后访问根节点)

      1.后序遍历递归解法
           1.先判断根节点是否是否为空,为空直接返回
           2.如果不为空,先访问左子树,再访问右子树,最后访问根节点的值,

           public void afterOrderedTraverse(Node<T> node){
                if(node == null){
                     return;
                }
                afterOrderedTraverse(node.left);
                afterOrderedTraverse(node.right);
                System.out.println(node.element);
           }
      3.后序遍历非递归版
           /**
            * 非递归后序遍历难点在于,需要使用一个节点记录上一次访问的节点是位于左子树还是右子树
            * 1.如果上一次访问的节点是左子树,则需先跳过根节点,先访问右子树
            * 2.如果上一次访问的节点是右子树,则可以访问根节点
            * 
            * 其他逻辑与中序遍历的非递归基本一致
            * 
            * @param root
            */
           public void afterOrderTraverseByStack(Node<T> root){
                if(root == null){
                     return ;
                }
                Stack<Node<T>> stack  = new Stack<>();
                Node<T> cur = root;
                Node<T> lastVisitNode = null;

                while(cur != null || !stack.isEmpty()){
                     while(cur != null){ //将根节点和左节点们加入栈中
                          stack.push(cur);
                          cur = cur.left;
                     }
                     Node<T> node = stack.pop();//返回当前的最左节点
                     //如果不存在右子树,或者已经访问过右子树,就可以直接访问当前节点;否则要先访问右子树
                     if(node.right == null || node.right == lastVisitNode){
                          System.out.println(node.element);
                          lastVisitNode = node; //更新上一次访问的节点
                          cur = null;
                     }
                     else{
                          //再把该节点返回栈中
                          stack.push(node);
                          //继续访问右子树
                          cur = node.right;
                     }
                }
           }

2.分层遍历二叉树也叫广度优先遍历(从左到右,从上到下访问树中的节点)

 解法:使用队列来解决
 使用队列来保存树的节点,首先将根节点(如果不为空的话,为空就直接返回了)加入队列来初始化队列。
 然后只有队列不为空就进入循环,在每一次循环中,取出并访问队首节点,
 然后将该节点的左,右子节点依次加入队列尾部(如果存在的话)
 public void traverseTreeInLevel(Node<T> root){
      if(root != null){
           return;
      }
      Queue<Node<T>> queue = new LinkedList<>();
      queue.add(root);
      while(!queue.isEmpty()){
           Node<T> node = queue.poll();
           System.out.println(node.element);
           if(node.left != null){
                queue.add(node.left);
           }
           if(node.right != null){
                queue.add(node.right);
           }
      }
 }

3.求二叉树中节点的个数

 递归求法:
 1.先判断根节点是否为空,为空返回0;
 2.如果不为空,当前树中节点的个数 = 1+左子树个数+右子树个数
 public int getAllNodesNumbers(Node<T> root){
      if(root == null){
           return 0;
      }
      int leftChildTreeNumbers = getAllNodesNumbers(root.left);
      int rightChildTreeNumbers = getAllNodesNumbers(root.right);
      return leftChildTreeNumbers + rightChildTreeNumbers+1;
 }

4.求二叉树的深度

 递归解法:
 1.先判断根节点是否为空,如果为空则返回0;
 2.如果根节点不为空,则树的深度 = Math.max(左子树的深度,右子树的深度)+1
 public int getTreeDepth(Node<T> root){
      if(root == null){
           return 0;
      }
      else{
           int leftChildTreeDepth = getTreeDepth(root.left);
           int rightChildTreeDepth = getTreeDepth(root.right);
           return Math.max(leftChildTreeDepth, rightChildTreeDepth)+1;
      }
 }

5.将二叉查找树变为有序的双向链表

 排序二叉树(英语:sorted binary tree),是指一棵空树或者具有下列性质的二叉树:
      若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
      若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
      任意节点的左、右子树也分别为二叉查找树;
      没有键值相等的节点。
 详见https://zh.wikipedia.org/wiki/%E4%BA%8C%E5%85%83%E6%90%9C%E5%B0%8B%E6%A8%B9
 /**
  * 使用非递归的中序遍历(借助栈来实现的),原因中序遍历二叉查找树刚好就是有序的
  * 把每一次遍历的节点与前一个节点连接起来
  *  
  * @param root
  * @return
  */
 public Node<T> convertBSTIntoList1(Node<T> root){
      if(root == null){
           return null;
      }
      Node<T> cur = root;
      Node<T> pre = root;
      boolean isFirst = true;
      Stack<Node<T>> stack = new Stack<>();
      while(cur!= null || !stack.isEmpty()){
           while(cur!=null){//将当前节点及其左节点们加入栈
                stack.push(cur);
                cur = cur.left;
           }
           cur = stack.pop();
           if(isFirst){
                root = cur;//让root指向中序遍历第一个节点,即最左节点
                pre = cur;
                isFirst = false;
           }
           else{
                pre.right = cur;//这两步是为了连接当前节点和前一个节点
                cur.left = pre;
                pre = cur;
           }
           cur = cur.right;//转移到当前节点的右子树
      }
      return root;
 }
 /*
  * 递归解法:
  * 1.将左子树构造成双链表,并返回链表头节点。
  * 2.定位至左子树双链表最后一个节点。
  * 3.如果左子树链表不为空的话,将当前root追加到左子树链表。
  * 4.将右子树构造成双链表,并返回链表头节点。
  * 5.如果右子树链表不为空的话,将该链表追加到root节点之后。
  * 6.根据左子树链表是否为空确定返回的节点。
  */
 public Node<T> convertBSTIntoList2(Node<T> root){
      if(root == null){
           return null;
      }
      if(root.left == null && root.right == null){
           return root;
      }

       //将左子树转换成双向链表并返回表头节点
       Node<T> left = convertBSTIntoList2(root.left);
       Node<T> point =left;
       //找到左子树的最后一个节点
       while(point !=null && point.right != null){
            point = point.right;
       }
       if(left != null){ //如果左子树不为空,将左子树的最后一个节点与root相连接
            point.right = root;
            root.left = point;
       }
      //将右子树转换成双向链表并返回表头节点
       Node<T> right = convertBSTIntoList2(root.right);
       if(right != null){
            //将root与右子树装换成的链表的表头节点相连
            root.right = right;
            right.left = root;
       }

       return left != null ? left : root;

 }


 /*
  * 递归对方法二的改进,没了第三步,换成使用内部属性来记录左子树的最后一个节点
  * 递归解法:
  * 1.将左子树构造成双链表,并返回链表头节点。
  * 2.定位至左子树双链表最后一个节点。
  * 
  * 4.将右子树构造成双链表,并返回链表头节点。
  * 5.如果右子树链表不为空的话,将该链表追加到root节点之后。
  * 6.根据左子树链表是否为空确定返回的节点。
  */
 protected Node<T> lastLeft;
 public Node<T> convertBSTIntoList3(Node<T> root){
      if(root == null){
           return null;
      }
      if(root.left == null && root.right == null){
           lastLeft = root; 
           return root;
      }

       //将左子树转换成双向链表并返回表头节点
       Node<T> left = convertBSTIntoList3(root.left);

       if(left != null){ //如果左子树不为空,将左子树的最后一个节点与root相连接
            lastLeft.right = root;
            root.left = lastLeft;
       }
      //将右子树转换成双向链表并返回表头节点
       Node<T> right = convertBSTIntoList3(root.right);
       if(right != null){
            //将root与右子树装换成的链表的表头节点相连
            root.right = right;
            right.left = root;
       }

       return left != null ? left : root;

 }

6.求二叉树第K层的节点个数

 递归解法:
 首先得明白这个道理,当K>1时,根节点的第K层相当于根节点左子树(右子树)的第K-1层;
 也就说,根节点的第K层节点个数=左子树的第k-1层的个数+右子树的第k-1层的个数

 解法:
      当二叉树根节点为空或者k<1时,直接返回0
      当 k=1,直接返回1
      如果前两个条件没有返回,则返回左子树的第k-1层的个数+右子树的第k-1层的个数
      public int getKthLevelNodesNumber(Node<T> root, int k){
      if(root == null || k < 1){
           return 0;
      }
      if(k == 1){
           return 1;
      }

      return getKthLevelNodesNumber(root.left, k-1)+getKthLevelNodesNumber(root.right, k-1);
      }

7.求二叉树叶子节点的个数

 递归解法:
 /**
  * 求叶子节点的个数
  *   叶子节点定义:左节点和右节点同时为空
  *   解法:
  *   划归为左子树和右子树的叶子节点之和的问题
  *   如果根节点为空则返回0;
  *   如果根节点的左右节点同时为null则返回1
  *    如果前两个条件没有返回,则返回根节点左子树的叶子数+根节点右子树的叶子数
  * @param args
  */
 public int getLeafNodeNumber(Node<T> root){
      if(root == null){
           return 0;
      }
      if(root.left == null && root.right == null){
           return 1;
      }
      return getLeafNodeNumber(root.left) + getLeafNodeNumber(root.right);
 }

8.判断两颗二叉树是否同构

 同构就是二叉树结构相同,不需要考虑数值
 /*判断两颗二叉树是否同构
 同构就是二叉树结构相同,不需要考虑数值
 递归解法:
 1.如果两颗树都为空,那么就返回真
 2.如果一棵树为空,另一颗不为空,那么就返回假
 3.如果前两个条件没有返回,则比较两个树的左子树和右子树,对结果取And返回
*/
 public boolean isBTStrutureSame(Node<T> tree1, Node<T> tree2){
      if( tree1 == null && tree2 == null){
           return true;
      }
      if((tree1 == null && tree2 != null) || (tree2 == null && tree1 != null) ){
           return false;
      }
      return isBTStrutureSame(tree1.left, tree2.left) && isBTStrutureSame(tree1.right, tree2.right);
 }

9.判断二叉树是不是平衡二叉树(AVL树)

平衡二叉树(Balanced Binary Tree)又被称为AVL树,
且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
 /**
  * 递归解法
  * 如果根节点为空,返回true
  * 如果根节点不为空,则获取左子树的高度,获取右子树的高度,相减去绝对值,如果大于1,返回false
  * 如果两个条件没有返回,则判断根节点的左右子树是否为ALV,对判断结果取And
  * @param root
  * @return
  */
 public boolean isALV(Node<T> root){
      if(root == null){
           return true; 
      }
      int leftChildTreeHeight = getTreeDepth(root.left);
      int rightChildTreeHeight = getTreeDepth(root.right);
      boolean result = Math.abs(leftChildTreeHeight - rightChildTreeHeight) > 1;
      if(result){
           return false;
      }
      return isALV(root.left) && isALV(root.right);
 }

10.求二叉树的镜像

  /**
  * 求二叉树的镜像
  * 思路:对二叉树的每一个节点,实现左右节点的交换
  * 递归解法:
  * 1.如果当前根节点为空,或者左右子节点同时为空则返回
  * 2.如果不满足上述条件,则交换左右子节点
  * 3.将当前根节点的左右子节点作为新的根节点,继续交换
  * 
  * @param args
  */
 public void mirrorBT(Node<T> root){
      if(root == null || (root.left == null && root.right == null)){
           return;
      }
      Node<T> leftRef = root.left; 
      root.left = root.right;
      root.right = leftRef;

      mirrorBT(root.left);
      mirrorBT(root.right);
 }

11. 求二叉树中节点的最大距离

 1.效率低的方法,但好理解
 即二叉树中相距最远的两个节点之间的距离。
 /**
  *这个两个节点的最大距离分为以下三种情况
  *两个节点在当前根节点的左右子树上,即经过根节点,为左子树的深度+右子树的深度
  *两个节点都在当前根节点的左子树上,即把当前根节点的左节点作为新的根节点,递归求这个两个节点的最大距离
  *两个节点都在当前根节点的右子树上,即把当前根节点的右节点作为新的根节点,递归求这个两个节点的最大距离
  *最后取三者的最大值
  * @param root
  * @return
  */
 public int getMaxDistance(Node<T> root){
      if(root == null){
           return 0;
      }
      int depthOfleftTree = getTreeDepth(root.left);//默认就加了根节点root.left,所以刚好等于到root的距离
      int depthOfRightTree = getTreeDepth(root.right);//同上
      int leftTreeMaxDistance = getMaxDistance(root.left);
      int rightTreeMaxDistance = getMaxDistance(root.right);
      return Math.max(Math.max(leftTreeMaxDistance,rightTreeMaxDistance), depthOfleftTree+depthOfRightTree);
 }
 2.使用成员变量与递归
 public class Solution {
      int max = 0;

     public int diameterOfBinaryTree(TreeNode root) {
         maxDepth(root);
         return max;
     }

     private int maxDepth(TreeNode root) {
         if (root == null) return 0;

         int left = maxDepth(root.left);
         int right = maxDepth(root.right);


         max = Math.max(max, left + right); //每次递归都检查是否需要更新max,

         return Math.max(left, right) + 1;
     }
 }

12.由中序遍历和后序遍历重建二叉树

 leetcode原题
 https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/#/description
 解题思路:
 1.主要分治法的使用
 2.从后序遍历的结果中,取出最后一个作为根节点的值
 然后找到该值在中序遍历的位置rootIndex,
      如果rootIndex>0,即该节点不是第一个,则表明存在左子树。
           取出inOrder和POSTOrder左子树的部分,递归调用
      如果rootIndex<inOrder.size()-1,即该节点不是最后一个,则表明存在右子树
           取出inOrder和POSTOrder右子树的部分,递归调用

 public class Solution {
 public TreeNode buildTree(int[] inorder, int[] postorder) {
      if(inorder == null || inorder.length == 0){
           return null;
      }
      TreeNode root = new TreeNode(0);
      ArrayList<Integer> inorderArrayList = new ArrayList<>();
      ArrayList<Integer> postorderArrayList = new ArrayList<>();
      for(int i = 0 ;i<inorder.length;i++){
           inorderArrayList.add(inorder[i]);
           postorderArrayList.add(postorder[i]);
      }
      build(root, inorderArrayList,postorderArrayList);
      return root;
 }
 private void build(TreeNode root, List<Integer> inOrder, List<Integer> postOrder){
      if(postOrder.size()<1){
           return;
      }
      root.val = postOrder.get(postOrder.size()-1);
      int rootIndex = inOrder.indexOf(root.val);
      if(rootIndex > 0){//表明存在左子树
           root.left = new TreeNode(0);
           //取出inOrder和POSTOrder左子树的部分,递归调用
           ArrayList<Integer> newInOrder =  new ArrayList<>(inOrder.subList(0, rootIndex));
           ArrayList<Integer> newPostOrder =  new ArrayList<>(postOrder.subList(0, rootIndex));
           build(root.left, newInOrder, newPostOrder);
      }
      if(rootIndex<inOrder.size()-1){//表明存在右子树
           root.right = new TreeNode(0);
           //取出inOrder和POSTOrder右子树的部分,递归调用
           ArrayList<Integer> newInOrder =  new ArrayList<>(inOrder.subList(rootIndex+1, postOrder.size()));
           ArrayList<Integer> newPostOrder =  new ArrayList<>(postOrder.subList(rootIndex, postOrder.size()-1));
           build(root.right, newInOrder, newPostOrder);
      }
 }
 }

13.判断一棵树是不是完全二叉树

 若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全
 二叉树。
 算法:按层次遍历二叉树(从左到右,从上到下),遇到某个节点。
 1.如果他的左子树为空,那么它的右子树也必须为空,并且排在该节点之后的所有节点
 左右节点都为空
 2.如过他的左子树不为空,右子树为空,那么排在该节点之后的所有节点
 左右节点都为空
 /*判断一棵树是不是完全二叉树
 定义:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全
 二叉树。
 算法:按层次遍历二叉树(从左到右,从上到下),遇到某个节点。
 1.如果他的左子树为空,那么它的右子树也必须为空,并且排在该节点之后的所有节点左右节点都为空
 2.如过他的左子树不为空,右子树为空,那么排在该节点之后的所有节点左右节点都为空
  */
 public boolean isComopleteBT(Node<T> root){
      Queue<Node<T>> queue = new LinkedList<>();
      queue.add(root);
      boolean flag = false; //true表明之后的节点必须都为叶子节点,即左右节点为空
      while(!queue.isEmpty()){
           Node<T> cur = queue.poll();
           if(!flag){
                if(cur.left!= null && cur.right !=null){
                     queue.add(cur.left);
                     queue.add(cur.right);
                }
                if(cur.left == null && cur.right!=null){
                     return false;
                }
                if(cur.left == null && cur.right == null){
                     flag = true;

                }
                if(cur.left != null && cur.right == null){
                     flag = true;
                     queue.add(cur.left);
                }
           }
           else{//必须都是叶子节点
                if(cur.left != null || cur.right!=null){
                     return false;
                }
           }
      }
      return true;
 }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值