617.给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
示例 1:
输入:root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:[3,4,5,5,4,null,7]
示例 2:输入:root1 = [1], root2 = [1,2]
输出:[2,2]
提示:
两棵树中的节点数目在范围 [0, 2000] 内
-104 <= Node.val <= 104
第一反应是递归
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if(root1==null&&root2==null){
return null;
}
if(root1==null){return root2;}
if(root2==null){return root1;}
//重叠当前节点
TreeNode temp=new TreeNode(root1.val+root2.val);
//重叠当前节点的左子树
temp.left=mergeTrees(root1.left,root2.left);
//重叠当前节点的右子树
temp.right=mergeTrees(root1.right,root2.right);
return temp;
}
}
答案为深度优先搜索和广度优先搜索,深度优先搜索同上。
1.广度优先搜索-三个队列。。
class Solution {
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if (t1 == null) {
return t2;
}
if (t2 == null) {
return t1;
}
TreeNode merged = new TreeNode(t1.val + t2.val);
Queue<TreeNode> queue = new LinkedList<TreeNode>();
Queue<TreeNode> queue1 = new LinkedList<TreeNode>();
Queue<TreeNode> queue2 = new LinkedList<TreeNode>();
queue.offer(merged);
queue1.offer(t1);
queue2.offer(t2);
while (!queue1.isEmpty() && !queue2.isEmpty()) {
TreeNode node = queue.poll(), node1 = queue1.poll(), node2 = queue2.poll();
TreeNode left1 = node1.left, left2 = node2.left, right1 = node1.right, right2 = node2.right;
if (left1 != null || left2 != null) {
if (left1 != null && left2 != null) {
TreeNode left = new TreeNode(left1.val + left2.val);
node.left = left;
queue.offer(left);
queue1.offer(left1);
queue2.offer(left2);
} else if (left1 != null) {
node.left = left1;
} else if (left2 != null) {
node.left = left2;
}
}
if (right1 != null || right2 != null) {
if (right1 != null && right2 != null) {
TreeNode right = new TreeNode(right1.val + right2.val);
node.right = right;
queue.offer(right);
queue1.offer(right1);
queue2.offer(right2);
} else if (right1 != null) {
node.right = right1;
} else {
node.right = right2;
}
}
}
return merged;
}
}
98.给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。
有效 二叉搜索树定义如下:
节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。
示例 1:
输入:root = [2,1,3]
输出:true
示例 2:
输入:root = [5,1,4,null,null,3,6]
输出:false
解释:根节点的值是 5 ,但是右子节点的值是 4 。
提示:
树中节点数目范围在[1, 104] 内
-231 <= Node.val <= 231 - 1
第一反应又是递归-但是没写出来,想不到重新写一个和比较当前节点的左子树和当前节点值这方法。
!!!错了很多次
class Solution {
public boolean isValidBST(TreeNode root) {
return isVBST(root,Long.MIN_VALUE,Long.MAX_VALUE);
}
public boolean isVBST(TreeNode root,long left,long right){
if(root==null){
return true;
}
if(root.val<=left||root.val>=right){
return false;
}
return isVBST(root.left,left,root.val)&&isVBST(root.right,root.val,right);
}
}
中序遍历法:
不要轻易使用全局变量,尤其是static修饰。不然有可能出错。
class Solution {
static List<Integer> list=new ArrayList<>();//去掉static就没有这个问题了
public boolean isValidBST(TreeNode root) {
//中序遍历树
infixOrder(root);
System.out.print(isASC(list));
return isASC(list);
}
public static void infixOrder(TreeNode root){
if(root==null){
return;
}
if(root.left!=null){
infixOrder(root.left);
}
list.add(root.val);
if(root.right!=null){
infixOrder(root.right);
}
}
//判断数组是否升序
public boolean isASC(List<Integer> list){
for(int i=0;i<list.size();i++){
if(i+1<list.size()&&list.get(i)>=list.get(i+1)){
return false;
}
}
return true;
}
}
class Solution {
public boolean isValidBST(TreeNode root) {
//中序遍历树
List<Integer> list=infixOrder(root,new ArrayList<Integer>());
return isASC(list);
}
public static List<Integer> infixOrder(TreeNode root,List<Integer> list){
if(root==null){
return null;
}
if(root.left!=null){
infixOrder(root.left,list);
}
list.add(root.val);
if(root.right!=null){
infixOrder(root.right,list);
}
return list;
}
//判断数组是否升序
public boolean isASC(List<Integer> list){
for(int i=0;i<list.size();i++){
if(i+1<list.size()&&list.get(i)>=list.get(i+1)){
return false;
}
}
return true;
}
}
338.给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
提示:
0 <= n <= 105
进阶:
很容易就能实现时间复杂度为 O(n log n) 的解决方案,你可以在线性时间复杂度 O(n) 内用一趟扫描解决此问题吗?
你能不使用任何内置函数解决此问题吗?(如,C++ 中的 __builtin_popcount )
class Solution {
public int[] countBits(int n) {
int[] res=new int[n+1];
for(int i=0;i<=n;i++){
res[i]=countBit(i);
}
return res;
}
public int countBit(int num){
//统计一个数字的二进制中1的个数
//1.先转二进制
String temp=Integer.toBinaryString(num);
char[] c=temp.toCharArray();
//2.统计c中1的个数
int count=0;
for(int i=0;i<c.length;i++){
if(c[i]=='1'){
count++;
}
}
return count;
}
}
被官方解法全方面虐
1.Brain Kernighan算法
本质就是疯狂与来统计1的个数:
对于任意整数 x,令 x=x&(x-1),该运算将 x 的二进制表示的最后一个 1 变成 0。因此,对 x 重复该操作,直到 x变成 0,则操作次数即为 x 的「一比特数」。
class Solution {
public int[] countBits(int n) {
int[] res=new int[n+1];
for(int i=0;i<=n;i++){
res[i]=countBit(i);
}
return res;
}
public int countBit(int num){
//统计一个数字的二进制中1的个数
int count=0;
while(num>0){
num=num&(num-1);
count++;
}
return count;
}
}
2.动态规划-最高有效位
3.动态规划-最低有效位
4.动态规划-最低设置位
这答案根本不想看啊,老和尚念经似的。。什么破玩意,不看了。
5.奇偶 和那劳什子最低有效位一样
class Solution {
public int[] countBits(int n) {
int[] res=new int[n+1];
//奇偶搞一哈
//1.奇数的1的个数=比他小的偶数的1的个数+1;
//2.偶数的1的个数=偶数/2的1的个数
for(int i=0;i<=n;i++){
if(i%2==0){
res[i]=res[i/2];
}else{
res[i]=res[i-1]+1;
}
}
return res;
}
}
class Solution {
public int[] countBits(int n) {
int[] res=new int[n+1];
//奇偶搞一哈
//1.奇数的1的个数=比他小的偶数的1的个数+1;
//2.偶数的1的个数=偶数/2的1的个数
for(int i=0;i<=n;i++){
res[i]=res[i/2]+i%2;
}
return res;
}
}
124.路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
示例 1:
输入:root = [1,2,3]
输出:6
解释:最优路径是 2 -> 1 -> 3 ,路径和为 2 + 1 + 3 = 6
示例 2:
输入:root = [-10,9,20,null,null,15,7]
输出:42
解释:最优路径是 15 -> 20 -> 7 ,路径和为 15 + 20 + 7 = 42
提示:
树中节点数目范围是 [1, 3 * 104]
-1000 <= Node.val <= 1000
class Solution {
//写一个全局变量计算路径值
int path=Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
maxPath(root);
return path;
}
public int maxPath(TreeNode root){
if(root==null){
return 0;
}
//左边告诉我左边的最大路径
int left=Math.max(maxPath(root.left),0);
//右边告诉我右边的最大路径
int right=Math.max(maxPath(root.right),0);
//我和左右一起
int temp=root.val+left+right;
//我目前最大的路径能不能大过我儿子,大过了就更新
path=Math.max(temp,path);
//这个节点的最大贡献值,往左往右或是停在原地
return root.val+Math.max(left,right);
}
}
279.给你一个整数 n ,返回 和为 n 的完全平方数的最少数量 。
完全平方数 是一个整数,其值等于另一个整数的平方;换句话说,其值等于一个整数自乘的积。例如,1、4、9 和 16 都是完全平方数,而 3 和 11 不是。
示例 1:
输入:n = 12
输出:3
解释:12 = 4 + 4 + 4
示例 2:输入:n = 13
输出:2
解释:13 = 4 + 9
提示:1 <= n <= 104
1.四平方和
class Solution {
public int numSquares(int n) {
if(isSquare(n)){return 1;}
if(isFourSquare(n)){return 4;}
if(isTwoSquare(n)){return 2;}
return 3;
}
//判断一个数是不是完全平方数
public boolean isSquare(int num){
int a=(int)Math.sqrt(num);
return a*a==num;
}
//判断一个数是不是由四个完全平方数的和
public boolean isFourSquare(int num){
while(num%4==0){
num/=4;
}
return num%8==7;
}
//判断一个数是不是两个平方数的和
public boolean isTwoSquare(int num){
for(int i=0;i<Math.sqrt(num)+1;i++){
int j=num-i*i;
if(isSquare(j)){
return true;
}
}
return false;
}
}
2.动态规划-讨厌动态规划
3.背包算法
class Solution {
public int numSquares(int n) {
int res=n;
int i=2;
while(i*i<=n){
int temp=n/(i*i);
int temp1=n%(i*i);
res=Math.min(res,temp+numSquares(temp1));
i++;
}
return res;
}
}
215.给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
提示:
1 <= k <= nums.length <= 104
-104 <= nums[i] <= 104
复习一下堆排序
class Solution {
public int findKthLargest(int[] nums, int k) {
//对重复元素的数组排序
heapSort(nums);
return nums[nums.length-k];
}
public void heapSort(int[] nums){
//排成大顶堆
for(int i=nums.length/2-1;i>=0;i--){
adjustHeap(nums,i,nums.length);
}
//排完第一次
for(int j=nums.length-1;j>0;j--){
int temp=nums[j];
nums[j]=nums[0];
nums[0]=temp;
adjustHeap(nums,0,j);
}
}
public void adjustHeap(int[] nums,int i,int length){
int temp=nums[i];
for(int k=2*i+1;k<length;k=2*k+1){
if(k+1<length&&nums[k]<nums[k+1]){
k++;
}
if(nums[k]>temp){
nums[i]=nums[k];
i=k;
}else{
break;
}
}
nums[i]=temp;
}
}
复习一下快速排序
class Solution {
public int findKthLargest(int[] nums, int k) {
//快排复习一下
quickSort(nums,0,nums.length-1);
for(int i=0;i<nums.length;i++){
System.out.print(nums[i]);
}
return nums[nums.length-k];
}
public void quickSort(int[] nums,int left,int right){
int l=left;
int r=right;
int piviot=nums[(l+r)/2];
while(l<r){
while(nums[l]<piviot){
l++;
}
while(nums[r]>piviot){
r--;
}
if(l>=r){
break;
}
int temp=nums[l];
nums[l]=nums[r];
nums[r]=temp;
if(nums[l]==piviot){
r--;
}
if(nums[r]==piviot){
l++;
}
}
if(l==r){
l++;
r--;
}
if(left<r){
quickSort(nums,left,r);
}
if(right>l){
quickSort(nums,l,right);
}
}
}
发现上面两种写法仿佛都不如答案用的堆排序和快速排序。
优化:
1、注意到每次运行结束当前标杆的位置都是定下来的,只需要某次标杆恰好是倒数第k个下标时,就找到了答案,后面怎么排无人在意。
2、为排除极端案例,使用随机化的pivot。
晕眩了。。
42.给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:输入:height = [4,2,0,3,2,5]
输出:9
提示:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
高低得给家人们表演一个暴力阴兵
直接一手超出时间限制。。
class Solution {
public int trap(int[] height) {
//类似俄罗斯方块
//找到height中最大的数
int max=0;
int result=0;
for(int i=0;i<height.length;i++){
if(height[i]>max){
max=height[i];
}
}
for(int i=1;i<=max;i++){
result+=square(height,i);
}
return result;
}
public int square(int[] height,int floor){
int left=0;
int right=height.length-1;
int res=0;
while(left<=height.length-1&&height[left]<floor){
left++;
}
while(right>=0&&height[right]<floor){
right--;
}
if(left==right){
return 0;
}
//从left到right所有小于floor的数都要算一个面积
for(int i=left;i<=right;i++){
if(height[i]<floor){
res++;
}
}
return res;
}
}
538.给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
提醒一下,二叉搜索树满足下列约束条件:
节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。
注意:本题和 1038: https://leetcode-cn.com/problems/binary-search-tree-to-greater-sum-tree/ 相同示例 1:
输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]
示例 2:输入:root = [0,null,1]
输出:[1,null,1]
示例 3:输入:root = [1,0,2]
输出:[3,3,2]
示例 4:输入:root = [3,2,4,1]
输出:[7,9,4,10]
提示:
树中的节点数介于 0 和 104 之间。
每个节点的值介于 -104 和 104 之间。
树中的所有值 互不相同 。
给定的树为二叉搜索树。
逆序中序遍历搞一哈;
class Solution {
int res=0;
public TreeNode convertBST(TreeNode root) {
infixOrder(root);
return root;
}
public void infixOrder(TreeNode root){
if(root==null){
return;
}
if(root.right!=null){
infixOrder(root.right);
}
res+=root.val;
root.val=res;
if(root.left!=null){
infixOrder(root.left);
}
}
}
答案还有一种莫里斯遍历。
莫里斯遍历的核心思想是用空闲指针减少空间开销。
class Solution {
int res=0;
public TreeNode convertBST(TreeNode root) {
TreeNode curr=root;
while(curr!=null){
if(curr.right==null){
res+=curr.val;
//System.out.print(res);
curr.val=res;
curr=curr.left;
}else{
TreeNode temp=curr.right;
//找到最左边的那个
while(temp.left!=null&&temp.left!=curr){
temp=temp.left;
}
//5指向4
if(temp.left==null){
temp.left=curr;
curr=curr.right;
}else{
//遍历结束了,得恢复
temp.left=null;
res+=curr.val;
curr.val=res;
curr=curr.left;
}
}
}
return root;
}
}
238.给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
提示:
2 <= nums.length <= 105
-30 <= nums[i] <= 30
保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内
进阶:你可以在 O(1) 的额外空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)。
class Solution {
public int[] productExceptSelf(int[] nums) {
int[] res=new int[nums.length];
res[0]=1;
//光×左边的值
for(int i=1;i<nums.length;i++){
res[i]=nums[i-1]*res[i-1];
}
int temp=1;
//res[nums.length-1]不动
for(int i=nums.length-1;i>0;i--){
temp=nums[i]*temp;
//System.out.print(temp+"\t");
res[i-1]*=temp;
}
return res;
}
}
739.给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指在第 i 天之后,才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。
示例 1:
输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例 2:输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
示例 3:输入: temperatures = [30,60,90]
输出: [1,1,0]
提示:
1 <= temperatures.length <= 105
30 <= temperatures[i] <= 100
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
//第一想法是双指针
int[] res=new int[temperatures.length];
for(int i=0;i<temperatures.length-1;i++){
for(int j=i;j<temperatures.length;j++){
if(temperatures[j]>temperatures[i]){
res[i]=j-i;
break;
}
}
}
return res;
}
}
题目解法单调栈,这个东西没学过。
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int length = temperatures.length;
int[] ans = new int[length];
//搞个栈
Deque<Integer> stack=new LinkedList<>();
for (int i = 0; i < length; i++) {
int temp = temperatures[i];
while(!stack.isEmpty()&&temp>temperatures[stack.peek()]){
int k=stack.pop();
ans[k]=i-k;
}
stack.push(i);
}
return ans;
}
}
448.给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
示例 1:
输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]
示例 2:输入:nums = [1,1]
输出:[2]
提示:
n == nums.length
1 <= n <= 105
1 <= nums[i] <= n
进阶:你能在不使用额外空间且时间复杂度为 O(n) 的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> res=new ArrayList<>();
int n=nums.length;
for(int i=0;i<nums.length;i++){
int k=(nums[i]-1)%n;
nums[k]+=n;
}
for(int i=0;i<n;i++){
System.out.print(nums[i]+"\t");
if(nums[i]<=n){
res.add(i+1);
}
}
return res;
}
}