455 分发饼干
先排序之后按顺序比较
class Solution {
public int findContentChildren(int[] g, int[] s) {
//57
Arrays.sort(g);
Arrays.sort(s);
int k=0;
int i=0;
int count=0;
while(k<s.length&&i<g.length){
if(g[i]<=s[k]){
count++;
i++;
k++;
}else k++;
}
return count;
}
}
376 摆动序列
顺序查找,如果不符合一个大一个小 就继续往后找
class Solution {
public int wiggleMaxLength(int[] nums) {
//13
int res = 1;
if(nums.length==1) return nums.length;
else if(nums.length==2&&nums[0]!=nums[1]) return 2;
int curdiff = -1;//当前的差值
int prediff = -1;//前一个的差值
int f = 0;//第一次要标记,因为两个初始化都是负数
for(int i=1;i<nums.length;i++){
curdiff = nums[i]-nums[i-1];
if(curdiff!=0&&f==0||curdiff>0&&prediff<0||curdiff<0&&prediff>0){
res++;
prediff = curdiff ;
f = 1;
}
}
return res;
}
}
55 跳跃游戏
核心思想是 跳几步无所谓,关键在于可跳的覆盖范围!
不一定非要明确一次究竟跳几步,每次取最大的跳跃步数,这个就是可以跳跃的覆盖范围。
这个范围内,别管是怎么跳的,反正一定可以跳过来。
那么这个问题就转化为跳跃覆盖范围究竟可不可以覆盖到终点!
class Solution {
public boolean canJump(int[] nums) {
//55
int len = 0;
if(nums.length==1) return true;
for(int i=0;i<=len;i++){//注意这里是小于等于len 因为跳不到更远的地方了
len=Math.max(len,i+nums[i]);
if(len>=nums.length-1) return true;
}
return false;
}
}
45 跳跃游戏2
感觉还是比较难
class Solution {
public int jump(int[] nums) {
//25
//从前往后看什么时候到了最大范围
int count = 0;
int cover = 0; //下一步覆盖的最远距离下标
int end = 0; // 当前覆盖的最远距离下标
if(nums.length==1) return 0;
for(int i=0;i < nums.length - 1;i++){
cover = Math.max(cover,i+nums[i]);
if(i==end){// 可达位置的改变次数就是跳跃次数
count++;
end = cover;//到达了上一次可达的最长位置之后,新的cover可能已经更新,不一定是最长位置向后加的
}
}
return count;
}
}
1005 k次反转后最大化的数组和
在某个笔试中做过,忘记有没有全ac了,主要思路是将正数和负数分开,然后每次将负数反转,如果负数个数不够,还要判断剩下次数是偶数就不用管,因为可以对同一个元素反转多次(可能是笔试的时候题目没有强调这个),奇数的话反转绝对值最小的那个
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
//29-51
ArrayList<Integer> pos = new ArrayList<Integer>();
ArrayList<Integer> minus = new ArrayList<Integer>();
int sum = 0;
for(int i=0;i<nums.length;i++){
if(nums[i]>=0) pos.add(nums[i]);
else minus.add(nums[i]);
sum+=nums[i];
}
Collections.sort(pos);
Collections.sort(minus);
if(minus.size()>=k){
for(int i=0;i<k;i++){
sum = sum-2*minus.get(i);//因为原本是负的
}
}else{//负数不够
for(int i=0;i<minus.size();i++){
sum = sum-2*minus.get(i);//因为原本是负的
}
// System.out.println(sum);
if((k-minus.size())%2==1){//偶数也不用管
if(pos.size()==0){
sum = sum+2*minus.get(minus.size()-1);//如果有0 相当于没变化
}else if(minus.size()==0){
sum = sum-2*pos.get(0);
}else{
sum = sum-2*Math.min(pos.get(0),Math.abs(minus.get(minus.size()-1)));
}
}
}
return sum;
}
}
134 加油站
改了很久,第一次的思路是找gas[i]-cost[i]最大的作为起来,然后有例子没过;第二次思路是在gas[i]-cost[i]最小的下一个作为起点,也有例子没过,后来才想起来,要找区间[]为负数的下一个,因为要尽可能让连续累加和最大的那个起始点。突然觉得这就是笔试中那些例子没过的题,如果这题没有给错误例子,估计还是改不出来
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
//22-50
//从最多的开始出发
int max = 0;
int index= -1;
int re = 0;
for(int i=0;i<gas.length;i++){
re+=gas[i]-cost[i];
if(re<0){
re = 0;
index=i;//找到最后一个更新为0的index 要找的就是index+1
}
else if(re>max){
max = re;
}
}
re = 0;
for(int i=index+1;i<gas.length;i++){
re+=gas[i]-cost[i];
if(re<0) return -1;
}
for(int i=0;i<=index;i++){
re+=gas[i]-cost[i];
if(re<0) return -1;
}
return index+1;
}
}
135 分糖果
因为几个月前做过,还是记得思路,只要从左往右比较右边比左边大,右边的就再加一个,再从右往左,左边比右边大的左边就再加一个。但是还是改了两次,第一次错在直接用sum去记录,没有用具体的数组记录每个人的糖果数量,第二次是发现要加了直接加,而不是在更少的那个基础上加。
class Solution {
public int candy(int[] ratings) {
//57-08
int sum = 0;
int[] res= new int[ratings.length];
Arrays.fill(res,1);
for(int i=0;i<ratings.length-1;i++){
if(ratings[i+1]>ratings[i]&&res[i+1]<=res[i]) res[i+1]=res[i]+1;
}
for(int i=ratings.length-2;i>=0;i--){
if(ratings[i+1]<ratings[i]&&res[i+1]>=res[i]) res[i]=res[i+1]+1;//要在少的基础上加,不能直接res[i]++
}
for(int i=0;i<res.length;i++){
sum+=res[i];
}
return sum;
}
}
860 柠檬水找零
class Solution {
public boolean lemonadeChange(int[] bills) {
//15-23
int wu = 0;
int shi = 0;
int ershi = 0;
for(int i=0;i<bills.length;i++){
if(bills[i]==5){
wu++;
continue;
}else if(bills[i] ==10){
if(wu<=0) return false;
wu--;
shi++;//用5去换了
continue;
}
else if(bills[i]==20){
if(shi>=1&&wu>=1){//优先用5和10
shi--;
wu--;
ershi++;
continue;
}else if(wu>=3){
wu-=3;
ershi++;
continue;
}else return false;
}
}
return true;
}
}
406 根据身高重建队列
这题一开始排序没想清楚,先排了h再排身高,然后后面插入的话还会再影响前面的结果,所以一开始的思路没写出来。看了题解才知道换个排序可以简单这么多!!!就是先按照身高从大到小排序,然后再排h从小到大,这样每次从身高大的开始插入,每次插入对应的k即可!因为后面是身高更小的,对前面排好的不会产生影响!!
class Solution {
public int[][] reconstructQueue(int[][] people) {
//26-17
//插入排序 暴力
Arrays.sort(people,(a,b)->b[0]-a[0]!=0?b[0]-a[0]:a[1]-b[1]);//身高从大到小排,k从小到大
ArrayList<int[]> res = new ArrayList<int[]>();
for(int[] tmp:people){
res.add(tmp[1],tmp);
}
return res.toArray(new int[people.length][]);
}
}
452 用最少数量的箭引爆气球
不用真的去remove,直接更新交集的右坐标,不然一开始一直想要remove的情况下思考不清楚要用什么数据结构。直接更新不用额外的数据结构。然后还有两个重要的点,也就是下面注释的两行,当数据量大到在int边界的时候不要用减号,直接判断。否则数据移除用减号判断就会出错。
class Solution {
public int findMinArrowShots(int[][] points) {
//交集 不用实际去模拟删除,直接更新,记录re
int res = 1;
if(points.length==1) return 1;
Arrays.sort(points,(a,b)->a[0]>b[0]?1:-1);//将起始位置从小到大排序 a[0]-b[0]这样的排序在2^31时会出错
for(int i=1;i<points.length;i++){
if(points[i][0]<=points[i-1][1]){
points[i][1] = Math.min(points[i][1], points[i-1][1]);//更新为交集右边界
}else{
res++;
}
}
return res;
}
}
435 无重叠区间
和上题其实是一样的,但是在写的时候遇到了排序的问题,用上一题的排序方法出出现执行错误,改成
Arrays.sort(intervals, (a,b)-> {
return Integer.compare(a[0],b[0]);
});
才成功通过
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
//和上题好像一样
//Arrays.sort(intervals,(a,b)->a[0]>b[0]?1:-1);//第二维排不排无所谓 会出现执行错误
Arrays.sort(intervals, (a,b)-> {
return Integer.compare(a[0],b[0]);
});
if(intervals.length==1) return 0;
int res = 0;
for(int i=1;i<intervals.length;i++){
if(intervals[i][0]<intervals[i-1][1]){
res++;
intervals[i][1] = Math.min(intervals[i][1],intervals[i-1][1]);
}
}
return res;
}
}
763 划分字母区间
一开始打算转换成上面的区间来计算,然后越写越麻烦,还是去看了题解,get到了思路,用一个数组去记录每个字母出现的最晚的index,然后遍历这个数组,如果当前索引已经等于最大的出现过的那个索引值,就可以在这里分割
class Solution {
public List<Integer> partitionLabels(String s) {
//53
//转换成 几个不重叠区间 会写得太麻烦
int[] hash = new int[26]; //记录最后一个出现的index
List<Integer> res = new ArrayList<Integer>();
for(int i=0;i<s.length();i++){
hash[s.charAt(i)-'a'] = i;
}
int max = -1;
int index=-1;
for(int i=0;i<s.length();i++){
max = Math.max(max,hash[s.charAt(i)-'a']);
if(i==max){
res.add(max-index);
index = i;
}
}
return res;
}
}
56 合并区间
一开始的思路是判断重叠就把上一个和现在的边界直接更新到原数组中,然后再判断不相等的加到res中,提交了之后发现有问题:出现两个以上需要重叠就不行,因为只能更新最近两个,更之前的就更新不到,这样就会出错。还是看了题解,换了一种添加res的方法:确定了之后不会再有重叠的之后再把之前的加入res中,这样保证每次加入的都不会在被更新,然后要记得最后一个记得添加
class Solution {
public int[][] merge(int[][] intervals) {
//51
if(intervals.length==1) return intervals;
Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
ArrayList<int[]> res = new ArrayList<int[]>();
int begin = intervals[0][0];//记录上一个区间的左
int end = intervals[0][1];//记录上一个区间的右
for(int i=1;i<intervals.length;i++){
if(intervals[i][0]<=end){//直接修改原数组
end = Math.max(intervals[i][1],end);
}else{//直到判断到右边的不会重叠了 再把前一个加入
res.add(new int[]{begin, end});
begin = intervals[i][0];
end = intervals[i][1];
}
}
res.add(new int[]{begin, end});//最后一个
return res.toArray(new int[res.size()][2]);
}
}
738 单调递增的数字
思路很简单,就是实现起来的时候一直没写好,一开始用StringBuilder,需要各种类型的转换,没写的很清楚。思路就是从右开始找第一个左边比右边大的,找到了就把该位的数字减1,一旦减了,后面的所有数字都要改成9
class Solution {
public int monotoneIncreasingDigits(int n) {
//21
if(n<=9) return n;
//用stringbuiler写了半天
String s = n+"";
char[] arr = s.toCharArray();
int index = arr.length;//从index开始全改为9
for(int i=arr.length-2;i>=0;i--){
if(arr[i]>arr[i+1]){
arr[i]--; //char可以直接-- 而且不会是0
index = i+1;
}
}
for(int i=index;i<arr.length;i++){
arr[i] = '9';
}
return Integer.parseInt(String.valueOf(arr));//先把char[]转为String 再转为int
}
}
968 监控二叉树
摄像头可以覆盖上中下三层,如果把摄像头放在叶子节点上,就浪费的一层的覆盖。
所以把摄像头放在叶子节点的父节点位置,才能充分利用摄像头的覆盖面积。
为啥要从叶子节点看呢?因为头结点放不放摄像头也就省下一个摄像头, 叶子节点放不放摄像头省下了的摄像头数量是指数阶别的。
所以我们要从下往上看,用后序遍历!!局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int res = 0;
public int minCameraCover(TreeNode root) {
if(af(root)==0){
res++;
}
return res;
}
// 0 表示无覆盖
// 1 表示 有摄像头
// 2 表示有覆盖
public int af(TreeNode root){
if(root==null) return 2;//空节点要默认为有覆盖,防止在叶子节点放
int left = af(root.left);
int right = af(root.right);
if(left==2&&right==2){
return 0;//左右都被覆盖 父节点无覆盖
}else if(left==0||right==0){
res++;
return 1;
}else return 2;
}
}
有注释:
class Solution {
int res=0;
public int minCameraCover(TreeNode root) {
// 对根节点的状态做检验,防止根节点是无覆盖状态 .
if(minCame(root)==0){
res++;
}
return res;
}
/**
节点的状态值:
0 表示无覆盖
1 表示 有摄像头
2 表示有覆盖
后序遍历,根据左右节点的情况,来判读 自己的状态
*/
public int minCame(TreeNode root){
if(root==null){
// 空节点默认为 有覆盖状态,避免在叶子节点上放摄像头
return 2;
}
int left=minCame(root.left);
int right=minCame(root.right);
// 如果左右节点都覆盖了的话, 那么本节点的状态就应该是无覆盖,没有摄像头
if(left==2&&right==2){
//(2,2)
return 0;
}else if(left==0||right==0){
// 左右节点都是无覆盖状态,那 根节点此时应该放一个摄像头
// (0,0) (0,1) (0,2) (1,0) (2,0)
// 状态值为 1 摄像头数 ++;
res++;
return 1;
}else{
// 左右节点的 状态为 (1,1) (1,2) (2,1) 也就是左右节点至少存在 1个摄像头,
// 那么本节点就是处于被覆盖状态
return 2;
}
}
}