目录:
一 .接引二叉树(一)
二 .二叉树相关oj题:
1. 检查两颗树是否相同2. 另一颗树的子树3. 翻转二叉树4. 判断一颗二叉树是否是平衡二叉树5. 对称二叉树6. 二叉树的构建及遍历7. 二叉树的分层遍历8. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先9. 根据一棵树的前序遍历与中序遍历构造二叉树10. 根据一棵树的中序遍历与后序遍历构造二叉树11. 二叉树创建字符串12. 二叉树前序非递归遍历实现13. 二叉树中序非递归遍历实现14. 二叉树后序非递归遍历实现
一 .接引二叉树(一):承接上篇,不清楚的可以回去看看:二叉树基础及实现(一)-CSDN博客
1. 判断一棵树是不是完全二叉树:
图解:
把二叉树元素放入队列中,如果最后队列里全部是元素,“null”,则该二叉树就是完全二叉树。
这里注意区分,空和元素,“null”的概念
代码:
public 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;}}
二 .二叉树相关oj题:
1. 检查两颗树是否相同:
题目:
图解:
代码:
public boolean isSameTree(TreeNode p, TreeNode q) {//先判断结构是否一样if(p != null && q == null || q != null && p == null) {return false;}//走到这里,两个都为空或者两个都不为空//排除都为空树,但是空树也是树if(p == null && q == null) {return true;}//走到这里两个都不为空//开始判断树,根的值是否一样if(p.val != q.val) {return false;}//树都不为空,树根的值一样就判断:左子树 && 右边是否一样if(isSameTree(p.left, q.left) && isSameTree(p.right, q.right)) {return true;}return false;}
2. 另一颗树的子树:
题目:
图解:
代码:
public boolean isSubtree(TreeNode root, TreeNode subRoot) {//这里是递归遍历root的返回条件,不能省略,我们要在root这颗大树上遍历找subRoot树if(root == null) {return false;}//判断整个树的根只有一次,就用isSameTreeif(isSameTree(subRoot, root)) return true;//来到第二层,这里要递归遍历root,//因为这棵树可能包含subRoot的子树if(isSubtree(root.left,subRoot)) return true;//root的左树if(isSubtree(root.right,subRoot)) return true;//root的右树return false;}//判断两棵树是否相同public boolean isSameTree(TreeNode p, TreeNode q) {//先= null) {return true;}//走到这里两个都不为空//开始判断树,根的值是否一样if(p.val != q.val) {return false;}//树都不为空,树根的值一样就判断:左子树 && 右边是否一样if(isSameTree(p.left, q.left) && isSameTree(p.right判断结构是否一样if(p != null && q == null || q != null && p == null) {return false;}//走到这里,两个都为空或者两个都不为空//排除都为空树,但是空树也是树if(p == null && q =, q.right)) {return true;}return false;}
3. 翻转二叉树:
题目:
图解:
代码:
public TreeNode invertTree(TreeNode root) {if(root == null) {return null;}TreeNode tmp = root.left;root.left = root.right;root.right = tmp;//递归前序遍历invertTree(root.left);invertTree(root.right);return root;}
4. 判断一颗二叉树是否是平衡二叉树:
题目:
方法一:时间复杂度为O(N^2,N的平方);因为我们求高度自己实现的函数getHeight,是先遍历到,最后一个二叉树再逐一返回,当(isBalanced(root.left) && isBalanced(root.right),去递归每一棵树的左右子树)时会重复去算高度。
图解:
代码:
时间复杂度为O(N^2,N的平方);因为我们求高度自己实现的函数getHeight,是先遍历放到最后一次二叉树再,逐一返回,当(isBalanced(root.left) && isBalanced(root.right);//去递归每一棵树的左右子树)时会重复去算高度*/
class Solution {public boolean isBalanced(TreeNode root) {if(root == null) {return true;}//判断二叉树节点是平衡树int leftHeight = getHeight(root.left);int rightHeight = getHeight(root.right);return Math.abs(leftHeight - rightHeight) < 2 && isBalanced(root.left) && isBalanced(root.right);//去递归每一棵树的左右子树}public int getHeight(TreeNode root) {if (root == null) {return 0;}//递归遍历左右树高度,遍历到root == null,再返回int leftTreeHeight = getHeight(root.left);int rightTreeHeight = getHeight(root.right);return Math.max(leftTreeHeight, rightTreeHeight) + 1;}
}
解法二:时间复杂度为O(N):
方法 : 返回时,顺便判断属不属于平衡二叉树,不属于返回-1,属于返回高度
代码;
public boolean isBalanced(TreeNode root) {if(root == null) {return true;}//返回的高度 >= 0则是平衡二叉树return getHeight(root) >= 0;}public int getHeight(TreeNode root) {if (root == null) {return 0;}//递归遍历左右树高度,遍历到root == null,再返回int leftTreeHeight = getHeight(root.left);if(leftTreeHeight < 0) {return -1;}int rightTreeHeight = getHeight(root.right);/**这里注意当二叉树遍历整棵树的最右边时,右边会返回-1,会破坏右边二叉树返回时的高度所以,rightTreeHeight的值要判断;如果等于-1,就不可以向上返回限制:(rightTreeHeight>=0)*/if(rightTreeHeight>=0 && Math.abs(leftTreeHeight-rightTreeHeight) <= 1){//是平衡二叉树,返回左右树做大值加1return Math.max(leftTreeHeight, rightTreeHeight) + 1;}else {return -1;}}
5. 对称二叉树:
题目:
图解:
代码:
public boolean isSymmetric(TreeNode root) {if(root == null) {return true;}return isSymmetric(root.left, root.right);}//封装起来判断,每个树的左右子树是否对称public boolean isSymmetric(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;}//开始判断对称,现在leftTree和rightTree都作为根return isSymmetric(leftTree.left,rightTree.right) &&isSymmetric(leftTree.right,rightTree.left);}
6. 二叉树的构建及遍历:
题目:
图解:
代码:
import java.util.Scanner;// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {static class TreeNode {public char val;public TreeNode left;public TreeNode right;public TreeNode(char val) {this.val = val;}}public static void main(String[] args) {Scanner in = new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseString str = in.nextLine();TreeNode root = createTree(str);inorderTree(root);}}//创建二叉树://遍历字符串的全局变量,局部变量递归时会把i置0public static int i = 0;public static TreeNode createTree(String str) {TreeNode root = null;if(str.charAt(i) != '#') {//先创建一个根节点root = new TreeNode(str.charAt(i));//创建好,i往后走,创建左右子树的各个节点i++;root.left = createTree(str);root.right = createTree(str);}else {i++;}return root;}//中序遍历打印二叉树public static void inorderTree(TreeNode root) {if(root == null) {return;}inorderTree(root.left);System.out.print(root.val + " ");inorderTree(root.right);}}
7. 二叉树的分层遍历 :
题目:
图解:
代码:
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()) {List<Integer> list = new ArrayList<>();//定义放在这里,等二叉树走完一层,才更新另外一层的sizeint size = queue.size();while(size != 0) {TreeNode cur = queue.poll();list.add(cur.val);if (cur.left != null) {queue.offer(cur.left);}if (cur.right != null) {queue.offer(cur.right);}size--;}//一维数组走完一个放入二维数组一个ret.add(list);}return ret;}
8. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先:
题目:
图解:解法一:
代码:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {if(root == null) {return null;}if(root == p || root == q) {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 && rightTree == null) {return leftTree;}else {return rightTree;}}
图解2:方法二:通过两个栈解决:
代码:
//最近公共祖先,方法二 :public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {Stack<TreeNode> stackP = new Stack<>();Stack<TreeNode> stackQ = new Stack<>();//1.把元素存入栈中getPath(root, p,stackP);getPath(root, q,stackQ);//2.求出那个栈元素多,让多的先出栈,直到两个栈元素一样多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()) {//stackP == stackQ,比较节点,如果比较的值是Integer类型,超过一定范围就要无效,要用equals比较if ( stackQ.peek() == stackP.peek()) {return stackP.peek();}else {//元素不一样就出栈stackP.pop();stackQ.pop();}}return null;}/**** @param root* @param node:要找路径的q p 节点* @param stack:存放节点的栈*///获取q, p路径public boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {if (root == null) {return false;}//节点放入栈中stack.push(root);//找到返回trueif (root == node) {return true;}//递归找q, p, 递归返回值retboolean ret = getPath(root.left,node,stack);//一层一层往上走if (ret) {return true;}ret = getPath(root.right,node,stack);if (ret) {return true;}//左右树遍历完没有找到,就把不是该路径的,节点弹出栈,并返回falsestack.pop();return false;}
9. 根据一棵树的前序遍历与中序遍历构造二叉树:
题目:
图解:
代码:
public TreeNode buildTree(int[] preorder, int[] inorder) {return buildTreeChild(preorder, inorder,0,inorder.length-1);}public int preIndex;public TreeNode buildTreeChild(int[] preorder , int[] inorder,int inbegin, int inend) {//没有左树或者右树if(inbegin > inend) {return null;}//new一个根节点TreeNode root = new TreeNode(preorder[preIndex]);//在中序遍历中从,前序遍历那里找到,代表所有子树的 根节点int rootIndex = findVal(inorder,inbegin,inend, preorder[preIndex]);//preIndex,要改为全局变量,左右每次递归是,才不会重置为0preIndex++;//rootInde用完才加加;//递归每一个创建左右子树root.left = buildTreeChild(preorder,inorder,inbegin,rootIndex-1);root.right = buildTreeChild(preorder,inorder,rootIndex+1,inend);return root;}private int findVal(int[] inorder,int inbegin, int inend,int val) {for(int i = inbegin; i<=inend; i++) {if(inorder[i] == val) {return i;}}return -1;}
10. 根据一棵树的中序遍历与后序遍历构造二叉树:
题目:
图解:
代码:
public TreeNode buildTree(int[] inorder, int[] postorder) {postorIndex = postorder.length-1;return buildTreeChild(inorder, postorder,0,inorder.length-1);}public int postorIndex;public TreeNode buildTreeChild(int[] inorder , int[] postorder,int inbegin, int inend) {//没有左树或者右树if(inbegin > inend) {return null;}//new一个根节点TreeNode root = new TreeNode(postorder[postorIndex]);//在中序遍历中从,前序遍历那里找到,代表所有子树的 根节点int rootIndex = findVal(inorder,inbegin,inend, postorder[postorIndex]);//preIndex,要改为全局变量,左右每次递归是,才不会重置为0postorIndex--;//rootInde用完才;//递归每一个创建左右子树root.right = buildTreeChild(inorder,postorder,rootIndex+1,inend);root.left = buildTreeChild(inorder,postorder,inbegin,rootIndex-1);return root;}private int findVal(int[] inorder,int inbegin, int inend,int val) {for(int i = inbegin; i<=inend; i++) {if(inorder[i] == val) {return i;}}return -1;}
11. 二叉树创建字符串:
题目:
图解:
代码:
public String tree2str(TreeNode root) {if(root == null) {return null;}StringBuilder stringbuilder = new StringBuilder();tree2strChild(root, stringbuilder);return stringbuilder.toString();}public void tree2strChild(TreeNode t, StringBuilder stringbuilder) {if(t == null) {return;}//先拼接根节点stringbuilder.append(t.val);if(t.left != null) {stringbuilder = stringbuilder.append("(");tree2strChild(t.left, stringbuilder);//递归拼接左边//每一颗子树走完,就拼接“)”stringbuilder.append(")");}else {if(t.right == null) {//左右都为空return;}else {//左边为空,右边不为空就拼接 “()”。stringbuilder.append("()");}}if(t.right != null) {stringbuilder = stringbuilder.append("(");tree2strChild(t.right, stringbuilder);//递归拼接右边//每一颗子树走完,就拼接“)”stringbuilder.append(")");}else {//右边为空return;}}
12. 二叉树前序非递归遍历实现 :
图解:这里用到栈
代码:
// 方法二:List<Integer> list = new LinkedList<>();if (root == null) {return list;}Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;while (cur != null || !stack.isEmpty()) {while (cur != null) {//根节点开始要放入栈中cur = stack.push(cur);list.add(cur.val);cur = cur.left;}//cur == null,就把栈里的元素,拿到top引用中TreeNode top = stack.pop();//cur再走top的右边cur = top.right;}return list;
13. 二叉树中序非递归遍历实现:
这里和上面方法一样,只是在出栈时候,放入链表或者顺序表,又或者打印。
代码:
public List<Integer> inorderTraversal(TreeNode root) {List<Integer> list = new ArrayList<>();if(root == null) {return list;}TreeNode cur = root;Stack<TreeNode> stack = new Stack<>();while(cur != null || !stack.isEmpty()) {while(cur != null) {cur = stack.push(cur);cur = cur.left;} //cur == null,就把栈里的元素,拿到top引用中TreeNode top = stack.pop();list.add(top.val);//cur再走top的右边cur = top.right;}return list;}
14. 二叉树后序非递归遍历实现:
这里思路也是大同小异,但是这里要定义一个,prev引用,记录一下被打印过的节点,左右cur就不会一直在,top的右边死循环了
图解:
代码:
public List<Integer> postorderTraversal(TreeNode root) {List<Integer> list = new LinkedList<>();//结束条件if (root == null) {return list;}Stack<TreeNode> stack = new Stack<>();TreeNode cur = root;//prev负责记录已经被放入list (被打印过) 的结点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) {list.add(top.val);stack.pop();prev = top;//prev负责记录被打印过的结点}else {cur = top.right;}}return list;}