1.遍历
二叉树使用前序遍历:
public class PreOrder {
public static void main(String[] args) {
PreOrder order = new PreOrder();
order.preOrder(order.initTree());
}
public void preOrder(TreeNode root) {
if (root != null) {
// 打印根结点
System.out.print(root.val + " ");
// 访问左子树
preOrder(root.left);
// 访问右子树
preOrder(root.right);
}
}
// 构造初始化树
public TreeNode initTree() {
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
treeNode.left.left = new TreeNode(4);
treeNode.left.right = new TreeNode(5);
treeNode.right.left = new TreeNode(6);
return treeNode;
}
}
非递归实现,应该怎么做呢?
我们可以借助栈结构实现,先将根结点放入栈中,然后循环以下操作直到栈为空:
- 取出栈顶第一个元素(结点),输出。
- 如果栈顶的第一个结点的右结点不为空,那么将右结点压入堆栈。
- 如果栈顶的第一个结点的左结点不为空,那么将左结点压入堆栈。
import java.util.Stack;
public class PreOrder {
public static void main(String[] args) {
PreOrder order = new PreOrder();
order.preOrder(order.initTree());
}
public void preOrder(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
// 树为空直接返回
if (root == null) return;
// 先将根结点压入栈中
stack.add(root);
// 循环以下代码直到栈为空
while (!stack.isEmpty()) {
// 取出栈顶元素,可以理解为当前根结点
TreeNode node = stack.pop();
// 输出当前根结点
System.out.print(node.val + " ");
// 先将右子树压栈
if (node.right != null) stack.push(node.right);
// 再将左子树压栈
if (node.left != null) stack.push(node.left);
}
}
// 构造初始化树
public TreeNode initTree() {
// 构造二叉树
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
treeNode.left.left = new TreeNode(4);
treeNode.left.right = new TreeNode(5);
treeNode.right.left = new TreeNode(6);
return treeNode;
}
}
二叉树使用中序遍历:
public class InOrder {
public static void main(String[] args) {
InOrder order = new InOrder();
order.inOrder(order.initTree());
}
public void inOrder(TreeNode root) {
if (root != null) {
// 左子树
inOrder(root.left);
// 打印当前根结点
System.out.print(root.val + " ");
// 右子树
inOrder(root.right);
}
}
// 构造初始化树
public TreeNode initTree() {
// 构造二叉树
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
treeNode.left.left = new TreeNode(4);
treeNode.left.right = new TreeNode(5);
treeNode.right.left = new TreeNode(6);
return treeNode;
}
}
中序遍历的非递归做法,和前序有点类似,同样借助堆栈。大致思路为,如果当前根结点不为空或者堆栈不为空,执行下面的循环:
如果当前根结点不为空,执行下面循环:
- 将当前结点压栈,并且当前结点置为其左结点。
- 如果当前的根节点为空,取出堆栈中的第一个元素,也就是没有遍历过的最左的子结点打印,然后将当前结点置为其右结点,相当于访问完了左子树和当前结点,之后接着访问右子树。
import java.util.Stack;
public class InOrder {
public static void main(String[] args) {
InOrder order = new InOrder();
order.inOrder(order.initTree());
}
public void inOrder(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
// 将当前结点压栈
stack.push(root);
// 遍历其左子树
root = root.left;
}
// 出栈
root = stack.pop();
// 当前结点被遍历
System.out.print(root.val + " ");
// 右结点
root = root.right;
}
}
// 构造初始化树
public TreeNode initTree() {
// 构造二叉树
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
treeNode.left.left = new TreeNode(4);
treeNode.left.right = new TreeNode(5);
treeNode.right.left = new TreeNode(6);
return treeNode;
}
}
二叉树使用后序遍历递归解法
递归的写法与前面的前序中序区别不大,只是递归的时候顺序不一样,与前面的类似:
public class PostOrder {
public static void main(String[] args) {
PostOrder order = new PostOrder();
order.postOrder(order.initTree());
}
public void postOrder(TreeNode root) {
if (root != null) {
// 左子树
postOrder(root.left);
// 右子树
postOrder(root.right);
// 打印根结点
System.out.print(root.val + " ");
}
}
// 构造初始化树
public TreeNode initTree() {
// 构造二叉树
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
treeNode.left.left = new TreeNode(4);
treeNode.left.right = new TreeNode(5);
treeNode.right.left = new TreeNode(6);
return treeNode;
}
}
二叉树使用后序非递归算法
import java.util.*;
public class PostOrder {
public static void main(String[] args) {
PostOrder order = new PostOrder();
order.postOrder(order.initTree());
}
public void postOrder(TreeNode root) {
List<Integer> res = new ArrayList<>();
if (root == null) return;
Stack<TreeNode> stack = new Stack<>();
// 先加入根节点
stack.add(root);
// 循环判断栈是否为空
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
res.add(0, node.val);
// 先压入左边,再压入右边,出栈的时候,就会先右边再左边
if (node.left != null) {
stack.add(node.left);
}
if (node.right != null) {
stack.add(node.right);
}
}
for (Integer item : res) {
System.out.print(item + " ");
}
}
// 构造初始化树
public TreeNode initTree() {
// 构造二叉树
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
treeNode.left.left = new TreeNode(4);
treeNode.left.right = new TreeNode(5);
treeNode.right.left = new TreeNode(6);
return treeNode;
}
}
二叉树层次遍历
import java.util.ArrayList;
import java.util.LinkedList;
public class LevelTree {
public static void main(String[] args) {
TreeNode root = initTree();
ArrayList<Integer> results = print(root);
results.forEach(e -> System.out.print(e + " "));
}
// 按照层次打印
public static ArrayList<Integer> print(TreeNode root) {
// 结果集
ArrayList<Integer> results = new ArrayList<>();
// 双向队列
LinkedList<TreeNode> queue = new LinkedList<>();
if (root != null) {
// 将根节点添加进去队列中
queue.add(root);
// 循环判断队列是否为空
while (!queue.isEmpty()) {
// 取出第一个元素
TreeNode treeNode = queue.pollFirst();
// 加入结果集
results.add(treeNode.val);
// 如果左子树不为空,则添加至队尾
if (treeNode.left != null) {
queue.add(treeNode.left);
}
// 如果右子树不为空,则添加至队尾
if (treeNode.right != null) {
queue.add(treeNode.right);
}
}
}
return results;
}
// 构造初始化树
public static TreeNode initTree() {
// 构造二叉树
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
treeNode.left.left = new TreeNode(4);
treeNode.left.right = new TreeNode(5);
treeNode.right.left = new TreeNode(6);
return treeNode;
}
}
根据两种遍历顺序,重构二叉树
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class ConstructTree {
public static void main(String[] args) {
int[] pre = { 1, 2, 4, 7, 3, 5, 6, 8 };
int[] in = { 4, 7, 2, 1, 5, 3, 8, 6 };
TreeNode root = constructBinaryTree(pre, in);
preOrder(root);
}
public static TreeNode constructBinaryTree(int[] pre, int[] in) {
if (pre == null || pre.length == 0 || in == null || in.length == 0) {
return null;
}
return constructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1);
}
static TreeNode constructBinaryTree(
int[] pre,
int startPre,
int endPre,
int[] in,
int startIn,
int endIn
) {
// 不符合条件直接返回null
if (startPre > endPre || startIn > endIn) {
return null;
}
// 构建根结点
TreeNode root = new TreeNode(pre[startPre]);
for (int index = startIn; index <= endIn; index++) {
if (in[index] == pre[startPre]) {
// 构建左子树
root.left =
constructBinaryTree(
pre,
startPre + 1,
startPre + (index - startIn),
in,
startIn,
index - 1
);
// 构建右子树
root.right =
constructBinaryTree(
pre,
(index - startIn) + startPre + 1,
endPre,
in,
index + 1,
endIn
);
break;
}
}
return root;
}
// 前序遍历二叉树
public static void preOrder(TreeNode root) {
if (root != null) {
// 打印根结点
System.out.print(root.val + " ");
// 访问左子树
preOrder(root.left);
// 访问右子树
preOrder(root.right);
}
}
}
苹果树的高度(求二叉树的高度)
public class TreeDepth {
public static void main(String[] args) {
TreeNode root = initTree();
System.out.println("树的高度为:" + treeDepth(root));
}
public static int treeDepth(TreeNode root) {
// 如果结点为 null,返回 0
if (root == null) return 0;
return Math.max(treeDepth(root.left), treeDepth(root.right)) + 1;
}
// 初始化树结构
public static TreeNode initTree() {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.right = new TreeNode(6);
root.left.right.left = new TreeNode(7);
return root;
}
}
镜子,你给这棵树照照
public class MirrorTree {
public static void main(String[] args) {
TreeNode root = initTree();
mirror(root);
preOrder(root);
}
public static void mirror(TreeNode root) {
if (root == null) {
return;
} else {
root = reverse(root);
}
}
// 左右反转
public static TreeNode reverse(TreeNode root) {
if (root == null) {
return root;
} else {
// 先分别反转左右子树,并保存
TreeNode left = reverse(root.right);
TreeNode right = reverse(root.left);
root.left = left;
root.right = right;
return root;
}
}
// 初始化树结构
public static TreeNode initTree() {
TreeNode root = new TreeNode(8);
root.left = new TreeNode(6);
root.right = new TreeNode(1);
root.left.left = new TreeNode(5);
root.left.right = new TreeNode(7);
root.right.left = new TreeNode(9);
root.right.right = new TreeNode(2);
return root;
}
// 前序遍历二叉树
public static void preOrder(TreeNode root) {
if (root != null) {
// 打印根结点
System.out.print(root.val + " ");
// 访问左子树
preOrder(root.left);
// 访问右子树
preOrder(root.right);
}
}
}
import java.util.Stack;
public class MirrorTree {
public static void main(String[] args) {
TreeNode root = initTree();
mirror(root);
preOrder(root);
}
public static void mirror(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> stack = new Stack<>();
// 先把根节点加入堆栈
stack.push(root);
// 判断根节点是否为空
while (!stack.isEmpty()) {
// 弹出第一个节点
TreeNode node = stack.pop();
// 保存左子树
TreeNode tempNode = node.left;
// 将左子树换成右子树
node.left = node.right;
// 右子树换成以前的左子树
node.right = tempNode;
// 添加左节点
if (node.left != null) {
stack.push(node.left);
}
// 添加右节点到堆栈
if (node.right != null) {
stack.push(node.right);
}
}
}
// 初始化树结构
public static TreeNode initTree() {
TreeNode root = new TreeNode(8);
root.left = new TreeNode(6);
root.right = new TreeNode(1);
root.left.left = new TreeNode(5);
root.left.right = new TreeNode(7);
root.right.left = new TreeNode(9);
root.right.right = new TreeNode(2);
return root;
}
// 前序遍历二叉树
public static void preOrder(TreeNode root) {
if (root != null) {
// 打印根结点
System.out.print(root.val + " ");
// 访问左子树
preOrder(root.left);
// 访问右子树
preOrder(root.right);
}
}
}
树的子结构
public class SubTree {
public static void main(String[] args) {
TreeNode root1 = initTree1();
TreeNode root2 = initTree2();
System.out.println("树2是否是树1的子树:" + hasSubtree(root1, root2));
}
public static boolean hasSubtree(TreeNode root1, TreeNode root2) {
// 只要一个为 null,则返回 false
if (root1 == null || root2 == null) {
return false;
}
// 从当前根结点比较
if (sameTree(root1, root2)) {
return true;
} else {
// 否则分别使用左子树或者右子树与 root2 匹配
return hasSubtree(root1.left, root2) || hasSubtree(root1.right, root2);
}
}
public static boolean sameTree(TreeNode root1, TreeNode root2) {
// 这里需要注意,当子结构遍历结束的时候,应该返回 true
if (root2 == null) {
return true;
}
if (root1 != null) {
// 两个节点相等
if (root1.val == root2.val) {
// 递归判断左子树和右子树也匹配
return (
sameTree(root1.left, root2.left) && sameTree(root1.right, root2.right)
);
} else {
return false;
}
}
return false;
}
// 构造主树
public static TreeNode initTree1() {
TreeNode root = new TreeNode(8);
root.left = new TreeNode(6);
root.right = new TreeNode(1);
root.left.left = new TreeNode(5);
root.left.right = new TreeNode(7);
root.right.left = new TreeNode(9);
root.right.right = new TreeNode(2);
return root;
}
// 构造子树
public static TreeNode initTree2() {
TreeNode root = new TreeNode(6);
root.left = new TreeNode(5);
root.right = new TreeNode(7);
return root;
}
}
不同结点的最近公共祖先结点
public class CommonAncestor {
public static void main(String[] args) {
TreeNode root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
// 初始化需要查找的结点
TreeNode p = new TreeNode(4);
TreeNode q = new TreeNode(5);
root.left.left = p;
root.left.right = q;
root.right.left = new TreeNode(6);
TreeNode commonNode = lowestCommonAncestor(root, p, q);
// 输出共同节点
System.out.println("common node: " + commonNode.val);
}
public static TreeNode lowestCommonAncestor(
TreeNode root,
TreeNode p,
TreeNode q
) {
// 如果任何一个等于null,直接返回
if (root == null || p == root || q == root) {
return root;
}
// 在左子树中查找
TreeNode left = lowestCommonAncestor(root.left, p, q);
// 在右子树中查找
TreeNode right = lowestCommonAncestor(root.right, p, q);
// 如果两边都查找到
if (left != null && right != null) {
// 返回其最近共同祖先
return root;
}
return left == null ? right : left;
}
}
树的路径求和 (回溯法)
一般而言,二叉树上的每一个结点,都存储着属于自己的数据,这个数据可以是数字,也可以是其他东西。而假设我们有一棵二叉树和一个整数,按字典顺序打印出二叉树中结点值的和为输入整数的所有路径。所谓路径,就是从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.stream.Collectors;
public class FindPathForTree {
public static void main(String[] args) {
TreeNode root = initTree1();
ArrayList<ArrayList<Integer>> results = findPath(root, 18);
results
.stream()
.forEach(
r -> {
r.forEach(e -> System.out.print(e + " --> "));
System.out.println("");
}
);
}
public static ArrayList<ArrayList<Integer>> findPath(
TreeNode root,
int target
) {
// 结果集
ArrayList<ArrayList<Integer>> results = new ArrayList<>();
// 队列
LinkedList<Integer> queue = new LinkedList<>();
int sum = 0;
if (root != null) {
// 从根节点开始
addDatas(root, target, results, queue, sum);
}
return results;
}
public static void addDatas(
TreeNode root,
int target,
ArrayList<ArrayList<Integer>> results,
LinkedList<Integer> queue,
Integer sum
) {
if (root != null) {
// 累加当前的值
sum = sum + root.val;
// 添加到队列中
queue.add(root.val);
// 如果和满足,并且是叶子节点
if (sum == target && root.left == null && root.right == null) {
addResult(results, queue);
} else {
// 递归左子树
addDatas(root.left, target, results, queue, sum);
// 递归右子树
addDatas(root.right, target, results, queue, sum);
}
// 处理完,弹出最后一个值,相当于回溯
queue.pollLast();
// 和的值也需要回溯
sum = sum - root.val;
}
}
public static void addResult(
ArrayList<ArrayList<Integer>> results,
Queue<Integer> queue
) {
// 将结果添加到最后的结果集
ArrayList<Integer> result = (ArrayList<Integer>) queue
.stream()
.collect(Collectors.toList());
results.add(result);
}
// 构造树
public static TreeNode initTree1() {
TreeNode root = new TreeNode(8);
root.left = new TreeNode(6);
root.right = new TreeNode(1);
root.left.left = new TreeNode(5);
root.left.right = new TreeNode(7);
root.right.left = new TreeNode(9);
root.right.right = new TreeNode(2);
return root;
}
}
平衡二叉树
我们都知道,平衡二叉树是一种特殊的二叉树,平衡二叉树(Balanced Binary Tree),具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过 1,并且左右两个子树都是一棵平衡二叉树。
public class BalanceTree {
// 测试代码
public static void main(String[] args) {
// 构造二叉树
TreeNode treeNode = new TreeNode(1);
treeNode.left = new TreeNode(2);
treeNode.right = new TreeNode(3);
treeNode.left.left = new TreeNode(4);
treeNode.left.right = new TreeNode(5);
treeNode.right.left = new TreeNode(6);
treeNode.right.right = new TreeNode(7);
boolean result = isBalance(treeNode);
System.out.println(result);
}
public static boolean isBalance(TreeNode root) {
if (root != null) {
// 左子树
int left = deep(root.left);
// 右子树
int right = deep(root.right);
// 高度差绝对值
if (Math.abs(left - right) > 1) {
return false;
} else {
// 递归判断左右子树
return isBalance(root.left) && isBalance(root.right);
}
}
return true;
}
// 求树的深度
public static int deep(TreeNode node) {
if (node == null) {
return 0;
}
// 左右子树最大高度+1
return Math.max(deep(node.left), deep(node.right)) + 1;
}
}
class TreeNode {
int val;
public TreeNode left;
public TreeNode right;
TreeNode(int val) {
this.val = val;
}
}