文章目录
![在这里插入图片描述](https://img-blog.csdnimg.cn/ebbcc3678e3348b69415e183771b8821.png)
一、入门
T455. 分发饼干
//小饼干给小胃口
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int count = 0;
int start = 0;
for(int i = 0;i<s.length&&start<g.length;i++){
if(s[i]>=g[start]){
start++;
count++;
}
}
return count;
}
}
二、中等
1. 序列问题
376. 摆动序列
class Solution {
//删除单调坡上的节点
public int wiggleMaxLength(int[] nums) {
if (nums.length == 1){return 1;}
//记录差值
int pre = 0;
int cur = 0;
int count = 1;
for(int i = 1;i<nums.length;i++){
cur = nums[i] - nums[i-1];
if ((cur>0&&pre<=0)|| (cur<0&&pre>=0)){//pre需要取等号
count++;
pre = cur;
}
}
return count;
}
}
2. 两个维度权衡问题
T406. 根据身高重建队列
class Solution {
//优先按身高高的people的k来插入 插入操作后的people满足队列属性
//二维分开处理
//先对身高排序 高的在前 若相同就看k k小的在前面
//再根据k插入
public int[][] reconstructQueue(int[][] people) {
//lambda表达式实现对二维数组的某一维排序
Arrays.sort(people,(x,y)->{
if(x[0] == y[0]){return x[1] - y[1];}
return y[0] - x[0];
});
LinkedList<int[]> que = new LinkedList<>();
for(int[] p:people){
que.add(p[1],p);
}
return que.toArray(new int[people.length][2]);
}
}
3. 股票问题
三、进阶
T53. 最大子数组和(dp或者贪心)
class Solution {
//局部最优:出现负数就重新开始
public int maxSubArray(int[] nums) {
if(nums.length == 1){return nums[0];}
int sum = Integer.MIN_VALUE;
int count = 0;
for(int i = 0;i<nums.length;i++){
count = count + nums[i];
sum = Math.max(count,sum);
if(count<0){
count=0;//出现负数 重新开始取数
}
}
return sum;
}
}
T134. 加油站
- 思路分析
总的耗油量必须大于0;
如果出现求和小于0,那么必然不是起点
class Solution {
//局部最优:当前累加rest[j]的和curSum一旦小于0,起始位置至少要是j+1,因为从j开始一定不行
public int canCompleteCircuit(int[] gas, int[] cost) {
int rest = 0;
int sum = 0;
int index = 0;
for(int i = 0;i<gas.length;i++){
rest += gas[i] - cost[i];
sum += gas[i] - cost[i];//加的油和消耗的油之差
if(rest<0){
rest = 0;
index = i + 1;
}
}
if(sum<0){return -1;}//此时必然不可能跑一周
return index;
}
}
T968. 监控二叉树
- 思路分析
从下往上看,局部最优:让叶子节点的父节点安摄像头,所用摄像头最少,整体最优:全部摄像头数量所用最少!
每个节点可能有3种状态:
该节点无覆盖
本节点有摄像头
本节点有覆盖 - 代码实现
//局部最优:让叶子节点的父节点放摄像头
//后序遍历:左右中
//传递进去的root节点需要由左右节点的状态来决定如何处理
//状态0:安装了监控;1:可以观察到,但没监控;2:观察不到
class Solution {
int sum = 0;
public int minCameraCover(TreeNode root) {
if(root == null){return 0;}
if(minHelper(root) == 2){sum++;}//如果根节点没装监控 需要加1
return sum;
}
public int minHelper(TreeNode root){
//终止条件
if(root == null){
return 1;
}
//计算左右节点的状态
int left = minHelper(root.left);
int right = minHelper(root.right);
//分情况处理root
if (left == 2 || right == 2){ //两个左右孩子有一个观察不到
sum++;
return 0;
}else if(left == 1 && right == 1){//左右孩子观察得到 但是没装
return 2;
}else{
return 1;
}
}
}
0. 区间问题
T55. 跳跃游戏 (能否到达)
class Solution {
//不管跳多少步 只要能覆盖到最后一层就好
public boolean canJump(int[] nums) {
if(nums.length == 1){return true;}
int cover = 0;
for(int i = 0;i<=cover;i++){ //当i超出cover范围后就无法跳跃了
cover = Math.max(cover,i+nums[i]);
if(cover>=nums.length-1){
return true;
}
}
return false;
}
}
T45. 跳跃游戏Ⅱ ****
- 思路分析
如果某一个作为 起跳点 的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为 起跳点。 - 可以对每一个能作为 起跳点 的格子都尝试跳一次,把 能跳到最远的距离 不断更新。
如果从这个 起跳点 起跳叫做第 1 次 跳跃,那么从后面 3 个格子起跳 都 可以叫做第 2 次 跳跃。
所以,当一次 跳跃 结束时,从下一个格子开始,到现在 能跳到最远的距离,都 是下一次 跳跃 的 起跳点。 - 对每一次 跳跃 用 for 循环来模拟。
- 跳完一次之后,更新下一次 起跳点 的范围。
- 在新的范围内跳,更新 能跳到最远的距离。
记录 跳跃 次数,如果跳到了终点,就得到了结果。
- 代码实现
class Solution {
public int jump(int[] nums) {
if(nums.length == 1) return 0;
int start = 0;//每一次跳跃的起点
int cover = nums[0];//最大覆盖范围,也是每次跳跃的终点
int step = 1;//记录步数,
while(cover < nums.length-1){//覆盖没有到达终点,一次while循环是一次跳跃
int tempStart = start;
int tempEnd = cover;
for(int i=tempStart;i<=tempEnd;i++){
if(i+nums[i]>cover){
cover = i+nums[i];
start = i + 1;
}
}
step++;
}
return step;
}
}
T452. 用最少数量的箭引爆气球
class Solution {
//局部最优:重叠在一起
//左边从小到大排序,更新右边界
public int findMinArrowShots(int[][] points) {
int count = 1;
Arrays.sort(points,(a,b)->{
return Integer.compare(a[0]-b[0]);
});
for(int i=1;i<points.length;i++){
if(points[i][0]>points[i-1][1]){
count++;//无重叠计数加1
}
else{
points[i][1] = Math.min(points[i][1],points[i-1][1]);//更新右边界
}
}
return count;
}
}
T435. 无重叠区间
思路分析:
排序(左边界从小到大),出现重叠就更新右边界
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
int count=0;//记录重复的区间
//排序:按第一个元素 升序排列
Arrays.sort(intervals,(x,y)->{
return x[0]-y[0];
});
for(int i=1;i<intervals.length;i++){
if(intervals[i-1][1]>intervals[i][0]){//前一个的右边界大于当前的左边界.出现重复了
count++;
intervals[i][1] = Math.min(intervals[i-1][1],intervals[i][1]);//更新右边界
}
}
return count;
}
}
T763. 划分字母区间
class Solution {
//找最大值切割
//画图分析
public List<Integer> partitionLabels(String s) {
int[] sList = new int[26];
char[] ch = s.toCharArray();
List<Integer> res = new ArrayList<>();
for(int i = 0;i<ch.length;i++){
sList[ch[i]-'a'] = i;
}
int last = -1;//记录最后一个索引,求长度可用
int maxIdx = 0;//记录最大边界
for(int i = 0;i<ch.length;i++){
maxIdx = Math.max(maxIdx,sList[ch[i]-'a']);//记录最大值
if (i == maxIdx){
res.add(i-last);
last=i;
}
}
return res;
}
}
T56. 合并区间
class Solution {
//i-1的右边界大于i的左边界就进行合并
public int[][] merge(int[][] intervals) {
Arrays.sort(intervals,(a,b)->Integer.compare(a[0],b[0]));
LinkedList<int[]> res = new LinkedList<>();//选择linkedLIST
res.add(intervals[0]);//存放当前区间,遍历的时候进行比较
for(int i = 1;i<intervals.length;i++){
if(intervals[i][0]<=res.getLast()[1]){//右边界小于等于左边界
int left = res.getLast()[0];
int right = Math.max(intervals[i][1],res.getLast()[1]);
res.removeLast();
res.add(new int[]{left,right});//更新左右边界
}else{
res.add(intervals[i]);//直接加入结果
}
}
return res.toArray(new int[res.size()][]);//linkedlist变成二维array
}
}