一、路径总和
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。如果存在,返回 true
;否则,返回 false
。
整体思路:到了哪一个结点 先更新一下TargetSum(TargetSum-=node.val),然后对结点进行分情况判断,叶子节点/非叶子节点
递归法:
1.返回值boolean 参数:TreeNode node int TargetSum
2.终止条件:遇到叶子节点进行判断,如果targetSum==0 返回true 相反 返回false
3.递归逻辑:
叶子节点if(node.left==null&&node.right==null)return targetSum==0;
非叶子节点:向左递归 if(node.left!=null)xxx();向右递归:if(node.right!=null)
代码:
public boolean hasPathSum(TreeNode root, int targetSum) {
if (root == null)
return false;
targetSum -= root.val;
if (root.left == null && root.right == null)
return targetSum == 0;
if (root.left != null) {
boolean left = hasPathSum(root.left,targetSum);
if (left) { // 已经找到
return true;
}
}
if (root.right != null) {
boolean right = hasPathSum(root.right,targetSum);
if (right) { // 已经找到
return true;
}
}
return false;
}
注意:在递归左右子节点(非叶子节点)的时候,为什么不能直接return hasPathSum(xxx);
如果返回的是一个false,你return的话,后面的代码就不会继续执行。如果返回的是true,那就找到了直接结束。
二、路径之和II(返回满足targetSum的路径)
仍然使用递归法,和第一题类似。
声明全局变量:List<List<Integer>> lists; List<Integer> list;
当遇到叶子节点的时候,要判断targetSum是否等于0,如果相等的话,那么就把list添加到lists中。然后左右子节点都要进行回溯。
代码:
class Solution {
private List<List<Integer>> lists;
private List<Integer> list;
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
lists=new ArrayList<>();
list=new ArrayList<>();
traversal(root,targetSum);
return lists;
}
public void traversal(TreeNode node ,int targetSum){
if(node==null)return;
targetSum-=node.val;
list.add(node.val);//先把节点的值放进去
if(node.left==null&&node.right==null&&targetSum==0){
lists.add(new ArrayList<>(list));
}
if(node.left!=null)traversal(node.left,targetSum);
if(node.right!=null)traversal(node.right,targetSum);
list.remove(list.size()-1);
}
}
三、最大二叉树
题目描述:
给定一个不重复的整数数组 nums
。 最大二叉树 可以用下面的算法从 nums
递归地构建:
- 创建一个根节点,其值为
nums
中的最大值。 - 递归地在最大值 左边 的 子数组前缀上 构建左子树。
- 递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums
构建的 最大二叉树 。
递归函数的返回值为TreeNode node int left int right
递归函数的终止条件为(left==right)return null;
递归逻辑:首先要找到数组中的最大值,然后左边部分作为根节点的左子树,右边部分作为根节点的右子树,然会返回根节点。但是left和right的规则一定要选好。我这里选的是左闭右开
代码:
class Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return tarversal(nums,0,nums.length);
}
public TreeNode tarversal(int[] nums,int start,int end){
if(start==end)return null;//终止条件
//找到最大值的下标
int maxIndex=start;
int max=nums[start];
for(int i=start;i<end;i++){
if(nums[i]>max){
max=nums[i];
maxIndex=i;
}
}
TreeNode root=new TreeNode(nums[maxIndex]);
//递归调用左子节点 比根节点的val值小的都在左边 从start->maxIndex 右开
root.left=tarversal(nums,start,maxIndex);
//递归调用右子节点 比根节点的val值大的都在右边,从maxIndex+1->end
root.right=tarversal(nums,maxIndex+1,end);
return root;
}
}
四、根据中序和后序遍历构建二叉树
思路:
1.根据后序的最后一个节点->得知根节点
2.然后从中序数组中找到根节点->左边就是左子树,右边就是右子树
3.左子树递归该函数
3.1左子树的后序数组的最后一个节点中->得知根节点
3.2 然后从中序数组中找到根节点->左边就是左子树,右边就是右子树
4 右子树递归该函数
4.1 右子树的后序的最后一个节点中->得知根节点
4.2 然后从中序数组中找到根节点->左边就是左子树,右边就是右子树
有一些细节需要注意:当left==right时,证明没有节点了
代码:
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
if(inorder==null||postorder==null)return null;
return buildHelper(inorder, 0, inorder.length, postorder, 0, postorder.length);
}
private TreeNode buildHelper(int[] inorder, int inorderStart, int inorderEnd, int[] postorder, int postorderStart, int postorderEnd){
//判断是否为空 随便一个就行
if(inorderStart==inorderEnd)return null;
//创建根节点
TreeNode root=new TreeNode(postorder[postorderEnd-1]);
//在中序数组中找到根节点的下标
int rootIndex=-1;
for(int i=0;i<inorderEnd;i++){
if(inorder[i]==root.val){
rootIndex=i;
break;
}
}
//左子树 中序遍历和后序遍历的起点和终点
int leftInorderStart=inorderStart;//左子树中序遍历的起点
int leftInorderEnd=rootIndex;//左子树中序遍历的终点
int leftPostorderStart=postorderStart;//左子树后序遍历的起点
int leftPostorderEnd=postorderStart+(rootIndex-inorderStart);左子树后序遍历的终点
root.left=buildHelper(inorder,leftInorderStart,leftInorderEnd,postorder,leftPostorderStart,leftPostorderEnd);
//右子树 中序遍历和后序遍历的起点和终点
int rightInorderStart=rootIndex+1;//右子树中序遍历的起点
int rightInorderEnd=inorderEnd;//右子树中序遍历的终点
int rightPostorderStart=leftPostorderEnd;//左子树后序遍历的起点
int rightPostorderEnd=postorderEnd-1;//左子树后序遍历的终点
root.right=buildHelper(inorder,rightInorderStart,rightInorderEnd,postorder,rightPostorderStart,rightPostorderEnd);
return root;
}
}
注意:所说的后序遍历的起点和终点指的是在数组中的位置,终点不是,左闭右开。
后序遍历的起点是根据中序遍历左子树节点的size来确定的。eg:
int leftPostorderEnd=postorderStart+(rootIndex-inorderStart);(后序遍历的起点)+size
五、合并二叉树
1.确定遍历方式 前序是最适合的 中左右
2.递归函数返回值:TreeNode 参数 TreeNode t1,TreeNode t2;
3.递归函数终止条件:
4.递归逻辑:
三种情况:左有右没,左没右有,左右都有。上面的return xx是下一层的left 或者 right
代码:
public TreeNode traversal(TreeNode node1,TreeNode node2){
if(node1==null)return node2;
if(node2==null)return node1;
if(node1!=null&&node2!=null)node1.val+=node2.val;
node1.left=traversal(node1.left,node2.left);
node2.right=traversal(node1.right,node2.right);
return node1
}
六、二叉搜索树中的搜索(递归法/迭代法)
二叉搜索树的性质:
- 非空左子树的所有键值小于其根结点的键值。
- 非空右子树的所有键值大于其根结点的键值。
- 左、右子树都是二叉搜索树。
递归法:
确定递归参数的返回值和参数:TreeNode node 参数:TreeNode node,int target
确定终止条件:node==null node.val==val
递归逻辑:node.val<val 向右边搜索 node.val>val 去左边搜索
代码:
public TreeNode searchBST(TreeNode root, int val) {
if(root==null)return null;
//节点相等
if(root.val==val)return root;
//节点小
if(root.val<val)return searchBST(root.right,val);
//节点大
if(root.val>val)return searchBST(root.left,val);
return null;
}
迭代法:
while(root!=null){
if(root.val>val)root=root.left;
if(root.val<val)root=root.right;
if(root.val==val)return root;
}
七、验证二叉搜索树(中序遍历!!!)
粗糙的解法:先通过中序遍历将val值都添加到一个集合中,然后判断集合是否的单调递增的。
代码:
class Solution {
List<Integer> list=new ArrayList<>();
public boolean isValidBST(TreeNode root) {
//终止条件
traversal(root);
for(int i=1;i<list.size();i++){
if(list.get(i)<=list.get(i-1))return false;
}
return true;
}
public void traversal(TreeNode root){
if(root==null)return ;
if(root.left!=null)traversal(root.left);
list.add(root.val);
if(root.right!=null)traversal(root.right);
}
}
灵活的解法:顶一个TreeNode pre 指向前一次遍历的节点,要求前一次遍历的节点要比现在的根节点的值要小 private TreeNode pre=null
Boolean leftFlag=traversal(node.left);
if(pre!=null&&pre.val>=node.val)return false;
pre=node;
Boolean rightFlag=traversal(node.right);
八、二叉搜索树的最小绝对差
最小绝对差一定出现在前后两个节点中。因为二叉搜索树在中序遍历之后,值是从小到大的。
解题思路跟上一个题一样,使用TreeNode pre=null;每次比较pre.val和root.val的差值.
代码:
class Solution {
private int min=Integer.MAX_VALUE;
private TreeNode pre=null;
public int getMinimumDifference(TreeNode root) {
traversal(root);
return min;
}
public void traversal(TreeNode root){
if(root==null)return ;
//中序遍历
if(root.left!=null)traversal(root.left);
if(pre!=null){
if((root.val-pre.val)<min)min=root.val-pre.val;
}
pre=root;
if(root.right!=null)traversal(root.right);
}
}
九、二叉搜索树中的众数
使用MaxCount全局变量 记录出现次数最多的 count记录当前结点数值出现的次数 results
思路:(还是使用pre指向上一个结点) 遇到的情况分为以下几种:
1.pre指向空,那么count=1 (刚开始)
2.pre.val和root.val不相等 count=1
3.如果pre.val==root.val count++
将众数添加到int数组中的过程:
if(count==MaxCount)results.add(node.val);如果次数等于最多的次数 那么就把它添加到数组中
如果count>MaxCount;那么直接把lists中的元素清除,然后add(node.val);Maxcount=count;更新一下出现最多的次数
代码:
class Solution {
private List<Integer> results;
private TreeNode pre;
private int count;
private int MaxCount;
public int[] findMode(TreeNode root) {
results = new ArrayList<>();
pre = new TreeNode();
count = 0;
MaxCount = 0;
traversal(root);
return ListToArray(results);
}
public void traversal(TreeNode root) {
if (root == null)
return;
// 中序遍历
traversal(root.left);
if (pre == null || root.val != pre.val)
count = 1;// 如果pre为空的话 刚开始比较 那么次数为1
if (pre != null && root.val == pre.val)
count++;// pre不为空 并且值相等 那么++
//把众数往集合加的逻辑
if (count == MaxCount)
results.add(root.val);
if (count > MaxCount){
results.clear();
results.add(root.val);
MaxCount=count;
}
pre = root;
traversal(root.right);
}
public int[] ListToArray(List<Integer> list) {
int[] result = new int[list.size()];
int index = 0;
for (int i : list) {
result[index++] = i;
}
return result;
}
}
十、二叉树的最近公共祖先(后序遍历)
递归法
1.确立递归函数的返回值TreeNode node,参数:TreeNode node,TreeNode p,TreeNode q
2.确立递归函数的终止条件:root==null root==q||root==p 直接返回root
3.递归逻辑:
1.如果root==p||root==q||root==null 就不用判断了 直接返回root
2.如果不等于的话 就要往下递归。
左:TreeNode left=traversal(left,p,q);看左子树有没有满足条件的节点
右:TreeNode right=traversal(right,p,q);右子树中有没有满足条件的节点
中:判断左右,两边都不为空 返回root
哪边不为空 返回哪边
代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) { // 递归结束条件
return root;
}
// 后序遍历
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null && right == null) { // 若未找到节点 p 或 q
return null;
}else if(left == null && right != null) { // 若找到一个节点
return right;
}else if(left != null && right == null) { // 若找到一个节点
return left;
}else { // 若找到两个节点
return root;
}
}
}