二叉树
来源:互联网 发布: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; }