1、分发饼干
https://leetcode.cn/problems/assign-cookies/description/
class Solution {
public int findContentChildren(int[] g, int[] s) {
if(s.length==0)
return 0;
//从小到大排序
int res=0;
Arrays.sort(g);
Arrays.sort(s);
if(s[0]>=g[g.length-1])
return s.length;
else{
for(int i=0,j=0;i<g.length&&j<s.length;j++){
if(g[i]<=s[j]){
res++;
i++;
}
}
}
return res;}}
2、摆动序列
https://leetcode.cn/problems/wiggle-subsequence/description/
class Solution {
public int wiggleMaxLength(int[] nums) {
int prediff=0;
int res=1;
int curdiff=0;
for(int i=0;i<nums.length-1;i++){
curdiff=nums[i+1]-nums[i];
if((prediff>=0&&curdiff<0)||(prediff<=0&&curdiff>0)){
res++;
prediff=curdiff;
}
}
return res;
}
}
默认最右边的是摆动序列加入结果集,默认上一个差为0。
只有摆动时才把 prediff=curdiff,不然如果是一直单调是有平坡就会出错。
3、最大字数和
https://leetcode.cn/problems/maximum-subarray/description/
class Solution {
public int maxSubArray(int[] nums) {
int sum=Integer.MIN_VALUE;
int count=0;
int len=nums.length;
if(len==1){
return nums[0];
}
for (int i = 0; i <len;i++) {
count+=nums[i];
sum=Math.max(sum,count);
if(count<0){
count=0;
}
}
return sum;}
}
我一直在考虑的是:如果当前值加的是一个负数,那么就重新加,但是这样不对,应该换一种思路:即 如果前面的连续和是一个负数的话,那么应该重新使用下一个数开始加,因为不论下一个数是什么,前面是负数,都会减少后续连续和。
还有一个问题:如果都是负数的话,怎么做?应该让 sum初始化为一个 最小值,这样就能在全部是负数中找到一个值。
4、买卖股票的最佳时机 II
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/description/
class Solution {
public int maxProfit(int[] prices) {
int res=0;
for(int i=0;i<prices.length-1;i++){
int diff=prices[i+1]-prices[i];
if(diff>0){
res+=diff;
}
}
return res;
}
}
直接ac
5、跳跃游戏
https://leetcode.cn/problems/jump-game/description/
class Solution {
public boolean canJump(int[] nums) {
if(nums.length==1)
return true;
int cover=0;
for(int i=0;i<=cover;i++){
cover=Math.max(i+nums[i],cover);
if(cover>=nums.length-1)
return true;
}
return false;
}
}
我刚开始的思路就是错误的,因为我一直在考虑如何到达终点,到底走几步,但是这样子就很复杂,应该换一个角度,即思考覆盖范围,只要覆盖范围能够大于等于最后的数,那么就一定能到达。
6、跳跃游戏Ⅱ
https://leetcode.cn/problems/jump-game-ii/description/
class Solution {
public int jump(int[] nums) {
if(nums.length==1)
return 0;
if(nums.length==1)
return 1;
int res=0;
int curcover=0;
int maxcover=0;
for (int i = 0; i <nums.length ; i++) {
maxcover=Math.max(i+nums[i],maxcover);
if(maxcover>=nums.length-1){
res++;
break;
}
if(i==curcover){
res++;
curcover=maxcover;
}
}
return res;
}
}
每走一步,取能够覆盖的最大范围。
7、K 次取反后最大化的数组和
https://leetcode.cn/problems/maximize-sum-of-array-after-k-negations/description/
class Solution {
public int largestSumAfterKNegations(int[] nums, int k) {
Arrays.sort(nums);
int sum=0;
//负数的情况下 消耗尽可能多的k
for (int i = 0; i <nums.length&&k>0 ; i++) {
if(nums[i]<0){
nums[i]=-nums[i];
k--;
}
else
break;
}
//没有负数了,但是k还没消耗完
if(k%2==1){
Arrays.sort(nums);
//奇数 则最小的正变负
nums[0]=-nums[0];
}
//偶数 则不用考虑
for (int i = 0; i <nums.length ; i++) {
sum+=nums[i];
}
return sum;
}
}
先用负数消耗k,如果没有消耗完,则用数组中最小的数 ,如果剩余k为偶数,则不用管,如果剩余数为奇数,则直接取一次反即可。
8、加油站
https://leetcode.cn/problems/gas-station/description/
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int curoil=0;
int sumoil=0;
int start=0;
for (int i = 0; i <gas.length ; i++) {
curoil+=gas[i]-cost[i];
sumoil+=gas[i]-cost[i];
if(curoil<0){
curoil=0;
start=i+1;
}
}
if(sumoil<0){
return -1;
}
return start;
}
}
如果curoil<0,那么从i+1开始走,
这个时候可能会有疑问?从[0,i]区间就没有一个点也会让curoil<0吗?答案是不会。
如果 curSum<0 说明 区间和1 + 区间和2 < 0, 那么 假设从上图中的位置开始计数curSum不会小于0的话,就是 区间和2>0。
区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择其实位置了。
9、分发糖果
https://leetcode.cn/problems/candy/description/
class Solution {
public int candy(int[] ratings) {
int[] candys=new int[ratings.length];//用来保存每个孩子的糖果数
candys[0]=1;
//从前往后 确定右边孩子是否比左边孩子得分高
for (int i = 1; i <ratings.length ; i++) {
if(ratings[i]>ratings[i-1]){
candys[i]=candys[i-1]+1;
}
else
candys[i]=1;
}
for (int i = ratings.length-2; i >=0 ; i--) {
if(ratings[i]>ratings[i+1]){
//左边评分高于右边
candys[i] = Math.max(candys[i], candys[i + 1] + 1);
}
}
int sum=0;
for (int i = 0; i <candys.length ; i++) {
sum+=candys[i];
}
return sum;
}
}
比较两端 分开比较
要注意 判断右边是否大于左边 要从前往后遍历 右边的结果依赖于前边的值。如果从后往前那么右边大于左边没有意义。比较右边小于左边可以。
判断左边是否大于右边,要从后往前遍历,
10、柠檬水找零
https://leetcode.cn/problems/lemonade-change/description/
class Solution {
public boolean lemonadeChange(int[] bills) {
int five = 0;
int ten = 0;
for (int i = 0; i < bills.length; i++) {
if (bills[i] == 5) {
five++;
} else if (bills[i] == 10) {
five--;
ten++;
} else if (bills[i] == 20) {
if (ten > 0) {
ten--;
five--;
} else {
five -= 3;
}
}
if (five < 0 || ten < 0) return false;
}
return true;
}
}
虽然我也通过了,但是我的比较复杂,我是使用了map,然后如果不符合,则false,但是可以通过上述方法,直接用变量,最后来判断。
11、根据身高重建队列
https://leetcode.cn/problems/queue-reconstruction-by-height/description/
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people,(a,b)->{
if(a[0]==b[0]){
return a[1]-b[1];
}
return b[0]-a[0];
});
LinkedList<int[]> que = new LinkedList<>();
for(int[] p:people){
que.add(p[1],p);
}
return que.toArray(new int[people.length][]);
}
}
这里有两个维度,一个身高,一个位置,两个维度时,我们不要同时考虑,而是先考虑一个。
因为是按照身高排序了,所以往前插入的值不会影响前面的位置,因为后边的身高一定比前面的小。
LinkedList.add 方法 第一个参数是下标index,第二个参数是value
12、用最少数量的箭引爆气球
https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/description/
class Solution {
public int findMinArrowShots(int[][] points) {
//左边界从小到大排序
Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
int counts=1;
for (int i = 1; i <points.length ; i++) {
if(points[i][0]>points[i-1][1]){
counts++;
}
else{
points[i][1]=Math.min(points[i][1],points[i-1][1]);
}
}
return counts;
}
}
左区间从小到大排序,因此之后只用比较上一个点右和该点的左,用这个来比较是否有重叠,并且我们还要判断后续的点是否与其也有重叠,因此,需要更新当前点的右点,为最小的点。
注意,溢出问题
1、使用 a[0] - b[0]
进行比较
Arrays.sort(points, (a, b) -> { return a[0] - b[0]; });
这种写法在多数情况下是有效的,因为它直接通过减法计算两个整数的差值,从而决定排序顺序。然而,这种方法存在一个潜在的 整数溢出 问题。
整数溢出的风险
定义:当两个整数相减的结果超出了
int
类型的表示范围(-2<sup>31</sup> 到 2<sup>31</sup>-1),就会发生溢出,导致结果不准确。
示例:
int a0 = Integer.MAX_VALUE; int b0 = -1; int result = a0 - b0; // 溢出,结果不正确
影响:如果
a[0]
和b[0]
的差值导致溢出,比较器将返回一个错误的值,破坏了排序的正确性,最终导致程序行为异常(如未通过测试用例)。
2. 使用 Integer.compare(a[0], b[0])
进行比较
Arrays.sort(points, (a, b) -> Integer.compare(a[0], b[0]));
这种写法使用了 Integer.compare
方法,它专门设计用于比较两个整数,且 不会出现溢出问题。Integer.compare
返回以下结果:
负数:如果
a[0] < b[0]
零:如果
a[0] == b[0]
正数:如果
a[0] > b[0]
因此,这种方式是 更安全、更可靠 的实现比较器的方式,尤其在处理可能导致溢出的情况下。
13、 无重叠区间
https://leetcode.cn/problems/non-overlapping-intervals/description/
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
//左边界从小到大排序
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
int counts=0;
for (int i = 1; i <intervals.length ; i++) {
if(intervals[i][0]<intervals[i-1][1]){
counts++;
intervals[i][1]=Math.min(intervals[i][1],intervals[i-1][1]);
intervals[i][0]=Math.min(intervals[i][0],intervals[i-1][0]);
}
}
return counts;
}
}
14、划分字母区间
https://leetcode.cn/problems/partition-labels/description/
思路:找每个字母出现的最远下标 如果i==当前最远下标 代表可以分割一次
class Solution {
public List<Integer> partitionLabels(String s) {
List<Integer> list=new LinkedList<>();//存放结果
int[] hash=new int[27];
for (int i = 0; i <s.length() ; i++) {
hash[s.charAt(i)-'a']=i;
}
int left=0;
int right=0;
for (int i = 0; i <s.length() ; i++) {
right=Math.max(right,hash[s.charAt(i)-'a']);
if(i==right){
list.add(right-left+1);
left=right+1;
}
}
return list;
}
}
15、合并区间
https://leetcode.cn/problems/merge-intervals/description/
class Solution {
public int[][] merge(int[][] intervals) {
//左边界从小到大排序
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0]));
LinkedList<int[]> res=new LinkedList<>();//存放结果
res.add(intervals[0]);
for (int i = 1; i <intervals.length ; i++) {
if(intervals[i][0]<=res.getLast()[1]){
//有重叠 合并
res.getLast()[1]=Math.max(res.getLast()[1],intervals[i][1]);
}
else
//没有重叠
res.add(intervals[i]);
}
return res.toArray(new int[res.size()][]);
}}
LinkedList 的 getLast() 函数
16、单调递增的数字
https://leetcode.cn/problems/monotone-increasing-digits/description/
从后往前 重点理解 flag作用
class Solution {
public int monotoneIncreasingDigits(int n) {
String s = String.valueOf(n);
char[] chars = s.toCharArray();
int flag=s.length();
for (int i = s.length()-1; i >0 ; i--) {
if (chars[i]<chars[i-1]){
chars[i-1]--;
flag=i;
}
}
for (int i = flag; i <s.length() ; i++) {
chars[i]='9';
}
String s1 = String.valueOf(chars);
Integer res = Integer.valueOf(s1);
return res;
}}
17、 监控二叉树
https://leetcode.cn/problems/binary-tree-cameras/description/
理解状态 和四种情况 尤其是最后一种 判断根节点的状态
/**
* 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;
* }
* }
*/
//0 无覆盖 1 有摄像头 2 有覆盖 默认 null 是2状态
class Solution {
int res=0;
public int minCameraCover(TreeNode root) {
int rootstate=cover(root);
if(rootstate==0){
res++;
}
return res;
}
public int cover(TreeNode root){
if(root==null) return 2;
//后序遍历
int left=cover(root.left);
int right=cover(root.right);
if(left==2&&right==2){
return 0;
}
if(left==0||right==0){
res++;
return 1;
}//2 和3 的情况不要反
if(left==1||right==1){
return 2;
}
return -1;//其实不会走到这里
}
}