遍历二叉树
static class TreeNode{
private int data;
private TreeNode left;
private TreeNode right;
public TreeNode(int a) {
this.data = a;
}
public TreeNode addL(int a) {
TreeNode left = new TreeNode(a);
this.left = left;
return left;
}
public TreeNode addR(int a) {
TreeNode right = new TreeNode(a);
this.right = right;
return right;
}
}
先序遍历
public static void pre(TreeNode node){
if(node == null) {
return;
}
System.out.print(node.data);
pre(node.left);
pre(node.right);
}
中序遍历
public static void in(TreeNode node){
if(node == null) {
return;
}
in(node.left);
System.out.print(node.data);
in(node.right);
}
后序遍历
public static void next(TreeNode node){
if(node == null) {
return;
}
next(node.left);
next(node.right);
System.out.print(node.data);
}
递归序
先序、中序、后序遍历的区别只是打印节点的位置不同。遍历二叉树时每个节点都会经过3次,第一次经过就打印就是先序,第二次经过打印就是中序,第三次经过打印就是后序。
相同的树
力扣地址: https://leetcode.cn/problems/same-tree
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
public static boolean sameTree(TreeNode t1, TreeNode t2){
if(t1 == null ^ t2 == null ) return false;
if(t1 == null && t2 == null) return true;
return t1.data == t2.data && sameTree(t1.left, t2.left)
&& sameTree(t1.right, t2.right);
}
对称的树
力扣地址:https://leetcode.cn/problems/symmetric-tree
public static boolean mirrorTree(TreeNode root) {
return mirrorTree(root, root);
}
private static boolean mirrorTree(TreeNode t1, TreeNode t2){
if(t1 == null ^ t2 == null) return false;
if(t1 == null && t2 == null) return true;
return t1.data == t2.data
&& mirrorTree(t1.left, t2.right)
&& mirrorTree(t1.right, t2.left);
}
获取二叉树的最大高度
力扣地址:https://leetcode.cn/problems/maximum-depth-of-binary-tree
public static int maxDeep(TreeNode root){
if(root == null) return 0;
return Math.max(maxDeep(root.left), maxDeep(root.right)) + 1;
}
根据先序和中序结果还原二叉树
力扣地址:https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal
重点: preorder 和 inorder 均 无重复 元素
思路:
- 先序列表中第一个元素一定是根节点root,在中序列表中找到根节点所在位置
- 将先序列表中左子树的列表 和 中序列表中左子树的列表进入递归 得到左子树的根节点,返回的节点就是root.left节点
- 将先序列表中右子树的列表 和 中序列表中右子树的列表进入递归 得到右子树的根节点,返回的节点就是root.right节点
public static TreeNode genByPreAndIn(int[] preArr, int[] inArr){
//边界判断
if(preArr == null || inArr == null || preArr.length != inArr.length){
return null;
}
HashMap<Integer, Integer> inArrMap = new HashMap<>();
for (int i = 0; i < inArr.length; i++) {
inArrMap.put(inArr[i], i);
}
return genByPreAndIn(preArr, 0, preArr.length -1, inArr, 0, inArr.length - 1, inArrMap);
}
/**
* 根据先序和中序的左、右边界返回创建二叉树节点
*/
private static TreeNode genByPreAndIn(int[] preArr, int L1, int R1,
int[] inArr, int L2, int R2,
HashMap<Integer, Integer> inArrMap) {
//如果L1 > R1 说明存在空的左子树或右子树
if(L1 > R1) {
return null;
}
TreeNode head = new TreeNode(preArr[L1]);
if(L1 == R1) {
return head;
}
int find = inArrMap.get(preArr[L1]);
//递归左子树 计算先序列表中左子树的范围 和 中序列表中左子树的范围
head.left = genByPreAndIn(preArr, L1 + 1, find - L2 + L1, inArr, L2, find - 1, inArrMap);
//递归右子树 计算先序列表中右子树的范围 和 中序列表中右子树的范围
head.right = genByPreAndIn(preArr, find -L2 + L1 + 1, R1, inArr, find + 1, R2, inArrMap);
return head;
}
层序遍历
力扣地址:https://leetcode.cn/problems/binary-tree-level-order-traversal-ii
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
思路:
- 创建一个链表实现的队列 第一次将root节点放入队列中
- 开始弹出root节点 并通过队列的size判断需要弹出多少次 ; 同时将弹出节点的左右节点放入队列中
- 弹出size个元素时就收集到当前层的结果中 并将结果插入到队列头节点
public List<List<Integer>> levelOrderBottom(TreeNode root) {
if(root == null ) return new LinkedList<>();
//定义队列收集二叉树节点
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
List<List<Integer>> result = new LinkedList<>();
while (!queue.isEmpty()) {
//先得到这一次循环需要处理的层节点
int size = queue.size();
List<Integer> ans = new ArrayList<>();
for (int i = 0; i < size; i++) {
TreeNode poll = queue.poll();
ans.add(poll.data);
if(poll.left != null) {
queue.offer(poll.left);
}
if(poll.right != null) {
queue.offer(poll.right);
}
}
result.add(0,ans);
}
return result;
}
平衡二叉树
力扣地址:https://leetcode.cn/problems/balanced-binary-tree/
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
思路:
- 要整个二叉树是平衡二叉树 只需要保证每个小的子树是平衡二叉树
- 使用递归收集每个子树是否平衡以及当前子树的高度
- 在上一层根据收集到的左右子树的信息 判断这一层子树是否平衡
/**
* 收集每一个子树的信息返回到父节点判断
*/
@Data
@AllArgsConstructor
private static class TreeInfo{
private boolean isBalance;
private int height;
}
public static boolean isBalance(TreeNode root){
return processIsBalance(root).isBalance;
}
private static TreeInfo processIsBalance(TreeNode root){
if(root == null) return new TreeInfo(true, 0);
TreeInfo left = processIsBalance(root.left);
TreeInfo right = processIsBalance(root.right);
int height = Math.max(left.height, right.height) + 1;
boolean isBalance = left.isBalance && right.isBalance && Math.abs(left.height - right.height) <= 1;
return new TreeInfo(isBalance, height);
}
搜索二叉树
力扣地址:https://leetcode.cn/problems/validate-binary-search-tree/
判断是否是搜索二叉树
搜索树定义:每个节点的左节点比他小 右节点比他大
中序遍历
思路:
- 中序遍历是左父右: 只要中序遍历的结果是递增的 一定是搜索二叉树
递归
思路:
- 使用递归收集每个子树是否是搜索树 收集当前树的最大值和最小值
- 在上一层根据收集到的左右子树的信息 判断这一层子树是否搜索二叉树:要比左树的最大值大 比右树的最小值小
public static boolean isSearch(TreeNode root){
return processIsSearch(root).isBST;
}
private static class BSTInfo{
private boolean isBST;
private int max;
private int min;
public BSTInfo(boolean isBST, int max, int min) {
this.isBST = isBST;
this.max = max;
this.min = min;
}
}
private static BSTInfo processIsSearch(TreeNode root){
//判空的处理交给上一层处理,不能返回最大值和最小值=0 因为父节点可能为负数,影响判断
if(root == null) return null;
BSTInfo left = processIsSearch(root.left);
BSTInfo right = processIsSearch(root.right);
//先把当前子树的最大值和最小值设置为当前节点的值
int max = root.data;
int min = root.data;
//如果左树不为空 将最大值和最小值更新
if(left != null) {
max = Math.max(max, left.max);
min = Math.min(min, left.min);
}
//如果右树不为空 将最大值和最小值更新
if(right != null) {
max = Math.max(max, right.max);
min = Math.min(min, right.min);
}
//判断左右子树是否搜索二叉树
boolean isBST = true;
if(left != null && !left.isBST) {
isBST = false;
}
if(right != null && !right.isBST) {
isBST = false;
}
//判断收集上来的左节点最大值是否小于root.data
boolean leftMaxLessX = left == null ? true : (left.max < root.data);
boolean rightMinMoreX = right == null ? true : (right.min > root.data);
if(!leftMaxLessX || !rightMinMoreX) {
isBST = false;
}
return new BSTInfo(isBST, max, min);
}
路径总和
力扣地址:https://leetcode.cn/problems/path-sum/
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。
public static boolean isPathSum = false;
public static boolean hasPathSum(TreeNode root, int sum){
if(root == null) return false;
isPathSum = false;
processHasPathSum(root, 0, sum);
return isPathSum;
}
private static void processHasPathSum(TreeNode root, int preSum, int sum) {
if(root.left == null && root.right == null) {
//叶子节点
if(preSum + root.data == sum) {
isPathSum = true;
}
return;
}
preSum += root.data;
//非叶子节点
if(root.left != null) {
processHasPathSum(root.left, preSum, sum);
}
if(root.right != null) {
processHasPathSum(root.right, preSum, sum);
}
}
路径总和 II
力扣地址:https://leetcode.cn/problems/path-sum-ii
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
/**
* 收集所有可能的路径和
* @param root
* @param sum
* @return
*/
public static List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> ans = new ArrayList<>();
if (root == null) {
return ans;
}
ArrayList<Integer> path = new ArrayList<>();
process(root, path, 0, sum, ans);
return ans;
}
/**
*
* @param x 当前节点
* @param path 当前遍历的路径
* @param preSum 当前遍历路径的累加和
* @param sum 求和判断
* @param ans 符合条件的结果
*/
public static void process(TreeNode x, List<Integer> path, int preSum, int sum, List<List<Integer>> ans) {
//是叶子节点的时候
if (x.left == null && x.right == null) {
//判断是否存在累加和
if (preSum + x.data == sum) {
path.add(x.data);
ans.add(copy(path));
//这里需要移除掉path中的叶子节点 因为这个路径已经计算完毕
path.remove(path.size() - 1);
}
return;
}
//是非叶节点
path.add(x.data);
preSum += x.data;
if (x.left != null) {
//遍历左树
process(x.left, path, preSum, sum, ans);
}
if (x.right != null) {
process(x.right, path, preSum, sum, ans);
}
//最后需要移除掉最后一个元素 因为已经回到了父节点,说明父节点下面的路径已经执行完
path.remove(path.size() - 1);
}
public static List<Integer> copy(List<Integer> path) {
List<Integer> ans = new ArrayList<>();
for (Integer num : path) {
ans.add(num);
}
return ans;
}