目录
1.二叉树前序递归与非递归遍历
递归代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class BinaryTree {
public void preorderTraversal(TreeNode root) {
if(root != null) {
System.out.println(root.val); //每次遍历都打印当前节点,然后再去打印左右子树节点,至于左右子树再怎么向下递归打印它的左右子树我们就不关心了
preorderTraversal(root.left); //递归调用,遍历左子树,打印左子树的节点
preorderTraversal(root.right); //递归调用遍历右子树,打印左子树节点
}
}
}
非递归代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class BinaryTree {
public void preorderTraversal(TreeNode root) {
if(root == null) { //空树直接返回,就不用遍历了
return;
}
Stack<TreeNode> s = new Stack<>();
s.push(root); //根节点入栈
while(!s.empty()) { //循环条件:栈不为空,因为栈不为空说明每遍历完
TreeNode cur = s.pop(); //此时栈顶保存的就是根节点,由于前序遍历先打印根节点,所以先将根节点出栈,
System.out.println(cur.val); //打印根节点
//由于栈先进后出特性,所以想先打印左子树节点,就将右子树节点先入栈
if(cur.right != null) {
s.push(cur.right);
}
if(cur.left != null) {
s.push(cur.left);
}
}
}
}
2.二叉树中序递归与非递归遍历
递归代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class BinaryTree {
public void inorderTraversal(TreeNode root) {
if(root != null) {
inorderTraversal(root.left); //一直递归调用到左子树为空,其实就相当于已经遍历了左子树
System.out.println(root.val); //所以按照次序,这里直接打印根节点
inorderTraversal(root.right); //然后递归调用,遍历右子树节点
}
}
}
非递归代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class BinaryTree {
public void inorderTraversal(TreeNode root) {
if(root == null) { //传的根节点为空直接返回,不用遍历了
return;
}
Stack<TreeNode> s = new Stack<>();
TreeNode cur = root; //定义一个引用指向头节点,用来遍历二叉树
while(cur!=null || !s.empty()) { //循环条件:cur!=null 或者栈不为空时说明还没遍历完
while(cur != null) {
s.push(cur); //尽可能将节点左子树依次压入栈中
cur = cur.left;
}
//此时栈顶的元素是最左侧的元素,它的左子树为空,相当于左子树已经遍历了,直接将其出栈并打印
cur = s.pop();
System.out.println(cur.val);
cur = cur.right; //左子树与根都遍历了,此时应该遍历它右节点了
}
}
}
3.二叉树后序递归与非递归遍历
递归代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class BinaryTree {
public void postorderTraversal(TreeNode root) {
if(root != null) {
postorderTraversal(root.left);
postorderTraversal(root.right);
System.out.println(root.val);
}
}
}
非递归代码:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class BinaryTree{
public void postorderTraversal(TreeNode root) {
Stack<TreeNode> s = new Stack<>();
TreeNode cur = root;
TreeNode lastTime = null; //用它来记录上一次遍历的节点,后面用来和当前节点比较,看看它是不是当前节点的右子树
while(cur != null || !s.empty()) { //cur不为空和栈不为空都说明节点没有遍历完,需要继续循环
while(cur != null) {
s.push(cur); //尽可能将节点左子树依次压入栈中
cur = cur.left;
}
//此时栈顶的元素是最左侧的元素,它的左子树为空,相当于左子树已经遍历了,但是不能直接打印。
//第一次当然很清楚是从左子树到达的根,因为它的左子树刚刚遍历完。
//但是后面有些有些情况无法判断它是从哪到的根,所以需要判断当前节点的上一个遍历节点是不是其右子树。
TreeNode temp = s.peek();
//右子树为空不用遍历可以直接打印,或者刚刚从右子树回来也可以直接打印当前节点
if(temp.right == null || temp.right == lastTime) {
System.out.println(temp.val);
lastTime = temp; //每次遍历都用lastTime保存,以便下次遍历时与节点右子树比较。
s.pop();
}else {
cur = temp.right; //右子树不为空且每遍历,就需要将右子树压栈遍历
}
}
}
}
4.二叉树的层序遍历
题目链接: leetcode102 二叉树的层序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> ret = new ArrayList<>();
if(root==null) {
return ret;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root); //先将根节点放入队列中
while(!q.isEmpty()) { //队列不为空说明还没遍历完
int size = q.size(); //size为当前层的元素个数
List<Integer> level = new ArrayList<>(size);//用level来保存一层元素
//一次性遍历一层的元素
for(int i=0;i<size;i++) {
TreeNode cur = q.poll(); //将当前层节点依次出队列
level.add(cur.val); //将当前层节点值放进level中
//如果节点有左孩子,就将左孩子入队列
if(cur.left != null) {
q.offer(cur.left);
}
//如果节点有右孩子,就将右孩子入队列
if(cur.right != null) {
q.offer(cur.right);
}
}
ret.add(level); //每遍历完一层就将当前层结果放入ret中
}
return ret;
}
}
5.检查两棵树是否相同
题目链接:leetcode100 两颗相同的树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p==null && q==null) {
return true;
}
if(p==null || q==null) {
return false;
}
//两棵树都不为空,就比较其节点值
if(p.val != q.val) {
return false;
}
//根节点比较之后就得继续检查他们的左右子树是否相同了
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
}
6.检查一棵树是否为另一棵树的子树
题目链接:leetcode572 另一棵树的子树
题目描述: 给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isSubtree(TreeNode s, TreeNode t) {
//s为空树时,没有子树
if(s == null) {
return false;
}
//空树为任何非空树子树
if(t == null) {
return true;
}
//两者都不为空时则判断其是否为同一棵树
if(isSameTree(s,t)) {
return true;
}
//递归判断t是否为s的左子树或者右子树
return isSubtree(s.left,t) || isSubtree(s.right,t);
}
public static boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q == null) {
return true;
}
if(p == null || q == null) {
return false;
}
if(p.val != q.val) {
return false;
}
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
}
7.求二叉树的最大深度
题目链接:leetcode104 二叉树最大深度
题目描述: 给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root == null) {
return 0;
}
return Math.max(maxDepth(root.left)+1,maxDepth(root.right)+1);
}
}
8.判断一颗二叉树是否是平衡二叉树
题目链接: leetcode110 平衡二叉树
题目描述: 给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。
解法一:自顶向下
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null) {
return true;
}
if(Math.abs(height(root.left)-height(root.right)) > 1) {
return false;
}
return isBalanced(root.left) && isBalanced(root.right);
}
private int height(TreeNode root) {
if(root == null) {
return 0;
}
return Math.max(height(root.left)+1,height(root.right)+1);
}
}
解法二:自底向上
方法一计算 height存在大量冗余。每次调用 height时,要同时计算其子树高度。但是自底向上计算,每个子树的高度只会计算一次。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
//递归到底判断左右子树是否平衡,不平衡返回-1,平衡就返回当前节点的高度。
class Solution {
public boolean isBalanced(TreeNode root) {
return height(root) != -1;
}
private int height(TreeNode root) {
if(root == null) {
return 0;
}
int leftHeight = height(root.left);
//如果其左子树不平衡就不用往下判断了,它肯定不平衡
if(leftHeight == -1) {
return -1;
}
int rightHeight = height(root.right);
//如果其右子树不平衡,也直接返回-1
if(rightHeight == -1) {
return -1;
}
return Math.abs(rightHeight-leftHeight) < 2 ? Math.max(leftHeight,rightHeight)+1 : -1;
}
}
9.检查一棵二叉树是否镜像对称
题目链接: leetcode101 对称二叉树
题目描述: 给定一个二叉树,检查它是否是镜像对称的。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
//可以把一棵树拆成两棵树看
class Solution {
public boolean isSymmetric(TreeNode root) {
return isSame(root,root);
}
private static boolean isSame(TreeNode a,TreeNode b) {
if(a == null && b == null) {
return true;
}
if(a == null || b == null) {
return false;
}
if(a.val != b.val) {
return false;
}
return isSame(a.left,b.right) && isSame(a.right,b.left);
}
}
10.求一棵二叉树的镜像
题目链接: leetcode27 二叉树的镜像
题目描述: 请完成一个函数,输入一个二叉树,该函数输出它的镜像。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
//求镜像,即将二叉树每个节点的左右子节点交换即可
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root == null) {
return null;
}
Stack<TreeNode> s = new Stack<>();
s.push(root);
while(!s.empty()) {
TreeNode node = s.pop();
if(node.left != null) {
s.push(node.left);
}
if(node.right != null) {
s.push(node.right);
}
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}
return root;
}
}
11.根据一棵树的前序遍历与中序遍历构造二叉树
题目链接: leetcode105 从前序和中序遍历序列构建二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
for(int i = 0;i < inorder.length;i++) {
if(inorder[i] == preorder[0]) {
TreeNode root = new TreeNode(preorder[0]);
//递归构建二叉树的左子树
root.left = buildTree(Arrays.copyOfRange(preorder,1,i+1),Arrays.copyOfRange(inorder,0,i));
//递归构建二叉树的右子树
root.right = buildTree(Arrays.copyOfRange(preorder,i+1,preorder.length),Arrays.copyOfRange(inorder,i+1,inorder.length));
//返回根节点
return root;
}
}
return null;
}
}
12.根据一棵树的中序遍历与后序遍历构造二叉树
题目链接: leetcode106 从中序和后序遍历序列构建二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
for(int i = 0; i < inorder.length; i++) {
if(inorder[i] == postorder[postorder.length-1]) {
TreeNode root = new TreeNode(postorder[postorder.length-1]);
//递归构建二叉树的左子树
root.left = buildTree(Arrays.copyOfRange(inorder,0,i),Arrays.copyOfRange(postorder,0,i));
//递归构建二叉树的右子树
root.right = buildTree(Arrays.copyOfRange(inorder,i+1,inorder.length),Arrays.copyOfRange(postorder,i,postorder.length-1));
//返回根节点
return root;
}
}
return null;
}
}
13.二叉树的最近公共祖先
题目链接: leetcode236 二叉树的最近公共祖先
题目描述: 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == p || root == q) {
return root;
}
Stack<TreeNode> s = new Stack<>();
TreeNode cur = root.left;
//设置两个标志位,看看p,q是否都在根节点左侧
Boolean flag1 = false;
Boolean flag2 = false;
while(cur != null || !s.empty()) {
while(cur != null) {
s.push(cur);
cur = cur.left;
}
cur = s.pop();
if(cur == p) {
flag1 = true;
}
if(cur == q) {
flag2 = true;
}
cur = cur.right;
}
//如果p,q都在左子树,就以左子树为根节点递归查找
//如果p,q都在右子树,就以右子树为根节点递归查找
//如果p,q在根节点两侧,那么根节点就是其最近公共祖先
if(flag1 && flag2) {
return lowestCommonAncestor(root.left,p,q);
}else if(!flag1 && !flag2){
return lowestCommonAncestor(root.right,p,q);
}
return root;
}
}