文章目录
- 二叉树搜索一条边/整个树的写法
- 二叉树
- 100. 相同的树
- 404. 左叶子之和
- 513. 找树左下角的值
- 112. 路径总和(简单) 返回值判断
- 113. 路径总和 II(中等)
- 106.从中序与后序遍历序列构造二叉树(中等)
- 105. 从前序与中序遍历序列构造二叉树(中等)
- 654. 最大二叉树(简单)
- 617. 合并二叉树
- 700. 二叉搜索树中的搜索
- 98. 验证二叉搜索树(中等)
- 530. 二叉搜索树的最小绝对差(简单)
- 501. 二叉搜索树中的众数
- 236. 二叉树的最近公共祖先
- 235. 二叉搜索树的最近公共祖先
- 701. 二叉搜索树中的插入操作
- 450. 删除二叉搜索树中的节点
- 669. 修剪二叉搜索树
- 108. 将有序数组转换为二叉搜索树(简单)
- 538. 把二叉搜索树转换为累加树
二叉树搜索一条边/整个树的写法
搜索一条边的写法:
if (递归函数(root->left)) return ;
if (递归函数(root->right)) return ;
搜索整个树的写法
left = 递归函数(root->left);
right = 递归函数(root->right);
left与right的逻辑处理;
二叉树
100. 相同的树
给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
输入:p = [1,2,3], q = [1,2,3]
输出:true
迭代法
public boolean isSameTree(TreeNode p, TreeNode q) {
return compare(p, q);
}
public boolean compare(TreeNode left, TreeNode right){
if(left == null && right == null) return true;
else if(left != null && right == null) return false;
else if(left == null && right != null) return false;
else if(left.val != right.val) return false;
boolean leftIsSame = compare(left.left, right.left);
boolean rightIsSame = compare(left.right, right.right);
return leftIsSame && rightIsSame;
}
404. 左叶子之和
给定二叉树的根节点 root ,返回所有左叶子之和。
递归法:
public int sumOfLeftLeaves(TreeNode root){
if (root == null) return 0;
int midValue = 0;
if (root.left != null && root.left.left == null && root.left.right == null){
midValue = root.left.val;
}
return midValue + sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
}
迭代:
513. 找树左下角的值
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
输入: root = [2,1,3]
输出: 1
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int res = 0;
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode poll = queue.poll();
if (i == 0) {
res = poll.val;
}
if (poll.left != null) {
queue.offer(poll.left);
}
if (poll.right != null) {
queue.offer(poll.right);
}
}
}
return res;
}
112. 路径总和(简单) 返回值判断
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
- 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
- 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先 (opens new window)中介绍)
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null) return false;
return traversal(root, targetSum - root.val);
}
public boolean traversal(TreeNode node, int count){
if(node.left == null && node.right == null && count == 0) return true;
if(node.left == null && node.right == null) return false;
if(node.left != null){
count -= node.left.val;
if(traversal(node.left, count)) return true;
count += node.left.val;
}
if(node.right != null){
count -= node.right.val;
if(traversal(node.right, count)) return true;
count += node.right.val;
}
return false;
}
113. 路径总和 II(中等)
给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径
List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
if(root == null) return resList;
List<Integer> list = new ArrayList<>();
traversal(root, list, targetSum - root.val);
return resList;
}
public void traversal(TreeNode node ,List<Integer> list, int count){
list.add(node.val);
if(node.left == null && node.right == null && count == 0){
resList.add(new ArrayList<>(list));
return;
}
if(node.left == null && node.right == null){
return;
}
if(node.left != null){
count -= node.left.val;
traversal(node.left, list, count);
list.remove(list.size() - 1);
count += node.left.val;
}
if(node.right != null){
count -= node.right.val;
traversal(node.right, list, count);
list.remove(list.size() - 1);
count += node.right.val;
}
return;
}
106.从中序与后序遍历序列构造二叉树(中等)
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
迭代思路:
- 若数组为0则为空节点
- 不为空,则取后序最后一个节点作为根节点
- 找到根节点在中序数组中的位置,作为切割点
- 切割中序数组,切成中序左数组和中序后数组
- 切割后序数组,切成后序左数组和后序右数组
- 递归处理左区间和右区间
重点:坚持循环不变量
首先切割中序数组(因为后序最后一个元素就是切割点,好找,坚持左闭右开)
左闭右开区间[0, delimiterIndex), [delimiterIndex,end)
然后切割后序数组,按照中序数组的大小来切割,切割成左数组和右数组
[0, leftInorder.size),[leftInorder.size, end)
递归:
root.left = traversal(leftInoreder, leftPostorder);
root.right = traversal(rightInorder, rightPostorder);
public TreeNode buildTree(int[] inorder, int[] postorder) {
return buildTree1(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
private TreeNode buildTree1(int[] inorder, int inLeft, int inRight,
int[] postorder, int postLeft, int postRight){
//没有元素了
if (inRight - inLeft < 1) return null;
if (inRight - inLeft == 1) return new TreeNode(inorder[inLeft]);
int rootVal = postorder[postRight - 1];
TreeNode root = new TreeNode(rootVal);
int rootIndex = 0;
for (int i = inLeft; i < inRight; i++) {
if (inorder[i] == rootVal){
rootIndex = i;
break;
}
}
System.out.println();
root.left = buildTree1(inorder, inLeft, rootIndex,
postorder, postLeft, postLeft + (rootIndex - inLeft));
root.right = buildTree1(inorder, rootIndex + 1, inRight,
postorder, postLeft + (rootIndex - inLeft), postRight - 1);
return root;
}
105. 从前序与中序遍历序列构造二叉树(中等)
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
思路:与后序+中序的思路一样,范围同样是左包右闭
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTree2(preorder, 0, preorder.length, inorder, 0, inorder.length);
}
public TreeNode buildTree2(int[] preorder, int preLeft, int preRight,int[] inorder, int inLeft, int inRight){
if(inRight - inLeft < 1) return null;
if(inRight - inLeft == 1) return new TreeNode(inorder[inLeft]);
int rootVal = preorder[preLeft];
TreeNode root = new TreeNode(rootVal);
int rootIndex = 0;
for(int i = inLeft; i < inRight; i++){
if(inorder[i] == rootVal){
rootIndex = i;
break;
}
}
root.left = buildTree2(preorder, preLeft + 1, preLeft + rootIndex - inLeft + 1, inorder, inLeft, rootIndex);
root.right = buildTree2(preorder, preLeft + rootIndex - inLeft + 1, preRight, inorder, rootIndex + 1, inRight);
return root;
}
654. 最大二叉树(简单)
给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
- 二叉树的根是数组中的最大元素。
- 左子树是通过数组中最大值左边部分构造出的最大二叉树
- 右子树是通过数组中最大值右边部分构造出的最大二叉树。
通过给定的数组构建最大二叉树,并且输出这个树的根节点。
public TreeNode constructMaximumBinaryTree(int[] nums) {
return travel(nums, 0, nums.length);
}
public TreeNode travel(int[] elems, int left, int right){
if(right - left < 1) return null;
if(right - left == 1) return new TreeNode(elems[left]);
int rootVal = Integer.MIN_VALUE;
int rootIndex = 0;
for(int i = left; i < right; i++){
if(elems[i] > rootVal){
rootVal = elems[i];
rootIndex = i;
}
}
TreeNode root = new TreeNode(rootVal);
root.left = travel(elems, left, rootIndex);
root.right = travel(elems, rootIndex + 1, right);
return root;
}
617. 合并二叉树
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。
思路:
使用前序
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1 == null) return root2;
if(root2 == null) return root1;
root1.val += root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
}
700. 二叉搜索树中的搜索
给定二叉搜索树(BST)的根节点和一个值。 你需要在BST中找到节点值等于给定值的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 NULL。
public TreeNode searchBST(TreeNode root, int val) {
Deque<TreeNode> deque = new LinkedList<>();
deque.offerLast(root);
while(!deque.isEmpty()){
int len = deque.size();
while(len > 0){
TreeNode tmp = deque.pollFirst();
if(tmp.val == val) return tmp;
if(tmp.left != null) deque.offerLast(tmp.left);
if(tmp.right != null) deque.offerLast(tmp.right);
len--;
}
}
return null;
}
98. 验证二叉搜索树(中等)
给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
思路:
要知道中序遍历下,输出的二叉搜索树节点的数值是有序序列。
有了这个特性,验证二叉搜索树,就相当于变成了判断一个序列是不是递增的了。
TreeNode max;
public boolean isValidBST(TreeNode root) {
if(root == null) return true;
//左
boolean left = isValidBST(root.left);
if(!left) return false;
//中
if(max != null && root.val <= max.val){
return false;
}
max = root;
//右
boolean right = isValidBST(root.right);
return right;
}
530. 二叉搜索树的最小绝对差(简单)
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。
差值是一个正数,其数值等于两值之差的绝对值。
递归:
TreeNode pre;
int res = Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
if(root == null) return 0;
inOrder(root);
return res;
}
public void inOrder(TreeNode root){
if(root == null) return;
inOrder(root.left);
if(pre != null) res = Math.min(res, root.val - pre.val);
pre = root;
inOrder(root.right);
}
迭代:
TreeNode pre;
Stack<TreeNode> stack;
public int getMinimumDifference(TreeNode root) {
if (root == null) return 0;
stack = new Stack<>();
TreeNode cur = root;
int result = Integer.MAX_VALUE;
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.push(cur); // 将访问的节点放进栈
cur = cur.left; // 左
}else {
cur = stack.pop();
if (pre != null) { // 中
result = Math.min(result, cur.val - pre.val);
}
pre = cur;
cur = cur.right; // 右
}
}
return result;
}
501. 二叉搜索树中的众数
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
如果树中有不止一个众数,可以按 任意顺序 返回。
假定 BST 满足如下定义:
- 结点左子树中所含节点的值 小于等于 当前节点的值
- 结点右子树中所含节点的值 大于等于 当前节点的值
- 左子树和右子树都是二叉搜索树
ArrayList<Integer> resList;
int maxCount;
int count;
TreeNode pre;
public int[] findMode(TreeNode root) {
resList = new ArrayList<>();
maxCount = 0;
count = 0;
pre = null;
findMode1(root);
int[] res = new int[resList.size()];
for(int i = 0; i < res.length; i++){
res[i] = resList.get(i);
}
return res;
}
public void findMode1(TreeNode root){
if(root == null) return;
findMode1(root.left);
int rootValue = root.val;
if(pre == null || rootValue != pre.val){
count = 1;
}else{
count++;
}
if(count > maxCount){
resList.clear();
resList.add(rootValue);
maxCount = count;
}else if(count == maxCount){
resList.add(rootValue);
}
pre = root;
findMode1(root.right);
}
236. 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
思路总结:
- 求最小公共祖先,需要从底向上遍历,那么二叉树,只能通过后序遍历(即:回溯)实现从低向上的遍历方式。
- 在回溯的过程中,必然要遍历整棵二叉树,即使已经找到结果了,依然要把其他节点遍历完,因为要使用递归函数的返回值(也就是代码中的left和right)做逻辑判断。
- 要理解如果返回值left为空,right不为空为什么要返回right,为什么可以用返回right传给上一层结果。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == q || root == p || root == null) return root;
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left != null && right != null) return root;
if(left != null && right == null) return left;
else if(left == null && right != null){return right;
}else{
return null;
}
}
235. 二叉搜索树的最近公共祖先
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
思路:其实只要从上到下遍历的时候,cur节点是数值在[p, q]区间中则说明该节点cur就是最近公共祖先了。
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null) return root;
if(root.val > p.val && root.val > q.val){
TreeNode left = lowestCommonAncestor(root.left, p, q);
if(left != null) return left;
}
if(root.val < p.val && root.val < q.val){
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(right != null) return right;
}
return root;
}
701. 二叉搜索树中的插入操作
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null) return new TreeNode(val);
travel(root, val);
return root;
}
public void travel(TreeNode root, int val){
if(root.val >= val){
if(root.left != null){
travel(root.left, val);
}else{
root.left = new TreeNode(val);
return;
}
}else{
if(root.right != null){
travel(root.right, val);
}else{
root.right = new TreeNode(val);
return;
}
}
}
450. 删除二叉搜索树中的节点
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。
public TreeNode deleteNode(TreeNode root, int key) {
if (root == null) return root;
if (root.val == key) {
if (root.left == null) {
return root.right;
} else if (root.right == null) {
return root.left;
} else {
TreeNode cur = root.right;
while (cur.left != null) {
cur = cur.left;
}
cur.left = root.left;
root = root.right;
return root;
}
}
if (root.val > key) root.left = deleteNode(root.left, key);
if (root.val < key) root.right = deleteNode(root.right, key);
return root;
}
669. 修剪二叉搜索树
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
思路:
若当前节点小于范围内的最小值,则递归右子树,并返回右子树符合范围的节点
对于大于同理
public TreeNode trimBST(TreeNode root, int low, int high) {
if(root == null) return root;
if(root.val < low){
return trimBST(root.right, low, high);
}
if(root.val > high){
return trimBST(root.left, low, high);
}
root.left = trimBST(root.left, low, high);
root.right = trimBST(root.right, low, high);
return root;
}
108. 将有序数组转换为二叉搜索树(简单)
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
public TreeNode sortedArrayToBST(int[] nums) {
return travel(nums, 0, nums.length - 1);
}
public TreeNode travel(int[] nums, int left, int right){
if(left > right) return null;
int mid = left + ((right - left) / 2);
TreeNode root = new TreeNode(nums[mid]);
root.left = travel(nums, left , mid - 1);
root.right = travel(nums, mid + 1, right);
return root;
}
538. 把二叉搜索树转换为累加树
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
//反中序遍历
int pre;
public TreeNode convertBST(TreeNode root) {
pre = 0;
travel(root);
return root;
}
public void travel(TreeNode cur){
if(cur == null) return;
travel(cur.right);
cur.val += pre;
pre = cur.val;
travel(cur.left);
}