二叉树

来源:互联网 发布:phpstudy如何配置域名 编辑:程序博客网 时间:2024/06/10 08:26

个人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; }
原创粉丝点击