题目来自《代码随想录》
https://leetcode-cn.com/problems/assign-cookies/
/**
* 执行用时:7 ms, 在所有 Java 提交中击败了99.93%的用户
* 内存消耗:42.5 MB, 在所有 Java 提交中击败了12.66%的用户
* @param g 孩子胃口值
* @param s 饼干值
* @return 能够满足多少个孩子的胃口
*/
public int findContentChildren2(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int start = 0;
int count = 0;
for (int i = 0; i < s.length && start < g.length; i++) {
if (s[i] >= g[start]) {
start++;
count++;
}
}
return count;
}
/**
* 1. 平什么版
* 执行用时:8 ms, 在所有 Java 提交中击败了23.59%的用户
* 内存消耗:42.1 MB, 在所有 Java 提交中击败了63.17%的用户
* @param g 孩子胃口值
* @param s 饼干值
* @return 能够满足多少个孩子的胃口
*/
public int findContentChildren(int[] g, int[] s) {
int ans = 0;
Arrays.sort(g);
Arrays.sort(s);
int ind = 0;
System.out.println(s.length);
for(int i=0; i<g.length; i++) {
System.out.println("i=" + i + ",ind:" + ind);
while(ind < s.length) {
System.out.println("第" + i + "个孩子胃口:" + g[i] + ",第" + ind + "个饼干:" + s[ind]);
if(g[i] <= s[ind]) {
System.out.println("满足胃口" );
ans++;
ind++;
System.out.println("满足后ind变为" + ind );
break;
} else {
ind++;
System.out.println("不满足后ind变为" + ind );
}
}
if(ind == s.length) break;
}
return ans;
}
https://leetcode-cn.com/problems/wiggle-subsequence/
/*
* 碰到【0,0,0】输出就是2,不对
*/
public int wiggleMaxLength(int[] nums) {
if(nums.length <=2) {
if( nums.length == 2 && nums[0] == nums[1]) return 1;
return nums.length;
}
int ans = 2;
int pre = 0; //前一对差
int next = 0; //下一个差
for(int i=1; i<nums.length-1; i++) {//判断从第二个到倒数第二个
pre = nums[i-1] - nums[i];
next = nums[i] - nums[i+1];
if((pre > 0 && next < 0) || (pre < 0 && next >0)) ans++;
}
return ans;
}
/*
* 正确答案
*/
public int wiggleMaxLength2(int[] nums) {
if(nums.length <=1) return nums.length;
int up = 1;
int down = 1;
for(int i=1; i<nums.length; i++) {
if(nums[i]-nums[i-1] > 0) up = down+1;
if(nums[i]-nums[i-1] < 0) down = up+1;
}
return Math.max(up, down);
}
https://leetcode-cn.com/problems/maximum-subarray/
//当前连续和为负数时,立马放弃
public int maxSubArray(int[] nums) {
int ans = Integer.MIN_VALUE;
int curans = 0;
for(int i=0; i<nums.length; i++) {
curans += nums[i];
if(curans > ans) ans = curans;
if(curans < 0) curans = 0;
}
return ans;
}
一遍过,蔑视中等题
/*
* 执行用时:1 ms, 在所有 Java 提交中击败了87.62%的用户
* 内存消耗:41.5 MB, 在所有 Java 提交中击败了11.16%的用户
*/
public int maxProfit(int[] prices) {
int ans = 0;
for(int i=1; i<prices.length; i++) {
if(prices[i]-prices[i-1] > 0) ans+=prices[i]-prices[i-1];
}
return ans;
}
https://leetcode-cn.com/problems/jump-game/
public boolean canJump(int[] nums) {
if(nums.length <= 1) return true;
return digui(nums, nums.length-1);
}
public boolean digui(int[] nums, int s) {
if(nums[0] >= s) {
System.out.println("从第" + s + "格可以跳到起点");
return true;
}
for(int i=s-1; i>=0; i--) {
if(nums[i] >= s-i) {
System.out.println("从第" + s + "格跳往第" + i + "格");
return digui(nums, i);
}
}
return false;
}
/*
* 1. 从后往前遍历所有,这种递归会超时的
*/
LinkedList<Integer> path = new LinkedList<>();
int ans = Integer.MAX_VALUE;
public int jump(int[] nums) {
if(nums.length == 1) return 0;
digui(nums, nums.length-1, 0);
return ans;
}
public void digui(int[] nums, int s, int curstep) {
if(nums[0] >= s) {
curstep++;
System.out.println("从第" + s + "格可以跳到起点,存入step为:" + curstep);
if(curstep < ans) ans = curstep;
path.add(curstep);
}
for(int i=s-1; i>=0; i--) {
if(nums[i] >= s-i) {
System.out.println("从第" + s + "格跳往第" + i + "格");
digui(nums, i, curstep+1);
}
}
}
/*
* 2. 还是从后往前,不递归,每次只迈最大步。但这玩意效率也不高
* 执行用时:14 ms, 在所有 Java 提交中击败了24.58%的用户
* 内存消耗:42 MB, 在所有 Java 提交中击败了13.65%的用户
*/
public int jump2(int[] nums) {
int ind = nums.length-1;
int step = 0;
while( ind > 0) {
for(int i=0; i<ind; i++) {
if(i+nums[i] >= ind) {
ind = i;
step++;
break;
}
}
}
return step;
}
/*
* 3. 没有达到我上一次跳跃的最远距离,就不跳
* 这过程中储存下一次能跳的最远距离
* 达到我上一次跳的最远距离了,就跳,次数加一
* 执行用时:2 ms, 在所有 Java 提交中击败了44.78%的用户
* 内存消耗:42.1 MB, 在所有 Java 提交中击败了11.47%的用户
*/
public int jump3(int[] nums) {
int res = 0;
int cur = 0;//当前最远
int next = 0; //下一步最远
for (int i = 0; i <= cur && cur < nums.length - 1; ++i) {//在条约范围内
next = Math.max(next, nums[i] + i);//这比的不是cur,比的是next
if(i == cur) {
cur = next;
res++;
}
}
return res;
}
public int largestSumAfterKNegations(int[] nums, int k) {
// 排序,把可能有的负数排到前面
Arrays.sort(nums);
int sum = 0;
for (int i = 0; i < nums.length; i++) {
// 贪心:如果是负数,而k还有盈余,就把负数反过来
if (nums[i] < 0 && k > 0) {
nums[i] = -1 * nums[i];
k--;
}
sum += nums[i];
}
Arrays.sort(nums);
// 1. 如果k没剩,那说明能转的负数都转正了,已经是最大和,返回sum
// 2. 如果k还剩偶数个就自己抵消掉,不用删减,
// 3. 如果k还剩奇数个就减掉2倍最小正数。
if( k%2 != 0) sum -= 2*nums[0];
return sum;
}
https://leetcode-cn.com/problems/gas-station/
/*
* 方法1:
* (1)如果gas和小于cost和,那就到不了(2)起点在gas和cost差最大的地方
* 答案错误!
* 如:gas = [5,8,2,8], cost = [6,5,6,6]
*/
public int canCompleteCircuit(int[] gas, int[] cost) {
int gassum = 0; // 能提供的汽油总和
int costsum = 0; // 消耗的汽油总和
int ans_index = 0;
int ans = 0;
for(int i=0; i<gas.length; i++) {
gassum += gas[i];
costsum += cost[i];
if( (gas[i]-cost[i]) > ans ) {
ans = gas[i]-cost[i];
ans_index = i;
}
}
if( gassum < costsum ) ans_index = -1;
return ans_index;
}
/*
* 方法2:
* 一直计算gas[i]-cost[i]的累加和,出现负数了,累加和置0,从下一位开始新的计算和记录
*/
public int canCompleteCircuit2(int[] gas, int[] cost) {
int index = 0; //最终答案
int tolsum = 0; //gas和-cost和
int cursum = 0; //gas[i]-cost[i]的累加和,出现负数了,累加和置0
for(int i=0; i<gas.length; i++) {
tolsum += gas[i]-cost[i];
cursum += gas[i]-cost[i];
if( cursum < 0 ) {
cursum = 0;
index = i+1;
}
}
if( tolsum < 0) return -1;
return index;
}
https://leetcode-cn.com/problems/candy/
/*
* 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
* 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。
*/
public int candy(int[] ratings) {
int[] candy = new int[ratings.length];
//1. 从左到右。右边比左边大,右边就加一。
candy[0] = 1;
for(int i=1; i<ratings.length; i++) {
candy[i] = 1;
if( ratings[i] > ratings[i-1]) {
System.out.println(i + "位置上的值比" + (i-1) + "位置上的大");
candy[i] = candy[i-1] + 1;
System.out.println(i + "位置上的值加完1为" + candy[i]);
}
}
//2. 从右到左。左边比右边大,左边就加一
//因为从左到右加过一次!所以要判断是从左边过来的大还是右边加一之后大!!
for(int i=ratings.length-2; i>=0; i--) {
if( ratings[i] > ratings[i+1]) {
candy[i] = Math.max(candy[i], candy[i+1] + 1);
}
}
//3. 求和
int ans = 0;
for( int i: candy) {
System.out.print(i + " ");
ans += i;
}
return ans;
}
https://leetcode-cn.com/problems/lemonade-change/
一遍过
/*
* 1. 10块的只能用5块的找
* 2. 20的:①10+5 ②5+5+5
* 注意这题是有顺序的,
*/
public boolean lemonadeChange(int[] bills) {
int num5 = 0;
int num10 = 0;
int num20 = 0;
for(int i=0; i<bills.length; i++) {
if( bills[i] == 5) num5++;
if( bills[i] == 10) {
if( num5 > 0) {//5块的够找10块的
num5--;
num10++;
}else return false; //不够找,false
}
if( bills[i] == 20) {
if( num10 > 0 && num5 > 0) {//1. 优先10+5的找
num5--;
num10--;
num20++;
}else if(num5 >= 3) {//2. 5块的有三张够找
num5 -= 3;
num20++;
}else return false; // 都不够找,false
}
}
return true;
}
https://leetcode-cn.com/problems/queue-reconstruction-by-height/
public int[][] reconstructQueue(int[][] people) {
// 身高从大到小排(身高相同k小的站前面)
Arrays.sort(people, (a, b) -> {
if (a[0] == b[0]) return a[1] - b[1];
return b[0] - a[0];
});
int len = people.length;
List<int[]> res= new ArrayList<>();
for(int i = 0;i<len;i++){
if (res.size() > people[i][1]){
//结果集中元素个数大于第i个人前面应有的人数时,将第i个人插入到结果集的 people[i][1]位置
res.add(people[i][1],people[i]);
}else{
//结果集中元素个数小于等于第i个人前面应有的人数时,将第i个人追加到结果集的后面
res.add(res.size(),people[i]);
}
}
return res.toArray(new int[len][2]);
}
https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/
//先排序,每有左边界超过上一个右边界的,答案加一
public int findMinArrowShots(int[][] points) {
if(points.length == 0) return 0;
Arrays.sort(points, (p1, p2) -> p1[1] < p2[1] ? -1 : 1);
int res = 1;
int pre = points[0][1];
for(int i=1; i<points.length; i++) {
if(points[i][0] > pre) {
res++;
pre = points[i][1];
}
}
return res;
}
https://leetcode-cn.com/problems/non-overlapping-intervals/
//按右闭区间从小到大排序
public int eraseOverlapIntervals(int[][] intervals) {
if(intervals.length == 0) return 0;
//得右边靠齐
Arrays.sort(intervals, new Comparator<int[]>() {
public int compare(int[] interval1, int[] interval2) {
return interval1[1] - interval2[1];
}
});
show(intervals);
int res = 0;
int pre = intervals[intervals.length-1][0];//最后一个的左区间
System.out.println("最后一个区间的左为:" + pre);
for(int i=intervals.length-2; i>=0; i--) {
if(intervals[i][1] > pre) {//当前右大于上一个左,重叠
System.out.println("倒数第" + (i+1) + "个区间的右后一个的左,即"
+ intervals[i][1] + "大于" + pre);
res++;//答案加一,pre不变
}else{//当前左小于等于上一个左,没重叠
pre = intervals[i][0];//pre换人
}
}
return res;
}
public void show(int[][] nums) {
for(int i=0; i<nums.length; i++) {
System.out.print("[");
for(int j=0; j<nums[i].length; j++) {
System.out.print(nums[i][j] + " ");
}
System.out.print("],");
}
System.out.print("\n");
}
//正解
public int eraseOverlapIntervals2(int[][] intervals) {
Arrays.sort(intervals, (a, b) -> {
if (a[0] == a[0]) return a[1] - b[1];
return a[0] - b[0];
});
int count = 0;
int edge = Integer.MIN_VALUE;
for (int i = 0; i < intervals.length; i++) {
if (edge <= intervals[i][0]) {
edge = intervals[i][1];
} else {
count++;
}
}
return count;
}
https://leetcode-cn.com/problems/partition-labels/
public List<Integer> partitionLabels(String s) {
List<Integer> list = new LinkedList<>(); //储存答案
int[] edge = new int[26];
char[] chars = s.toCharArray(); //统计每个字符出现的最后位置存入edge中
for (int i = 0; i < chars.length; i++) {
edge[chars[i] - 'a'] = i;
}
int idx = 0;//当前字符最远边界
int last = -1;//已经分割出去的片段总长度片段长度
for (int i = 0; i < chars.length; i++) {
//idx表示当前片段最小边界 = max(上个字符最远位置,当前字符最远位置)
idx = Math.max(idx,edge[chars[i] - 'a']);
if (i == idx) {//!!!如果当前字符最远位置和当前字符位置一样,得到答案
list.add(i - last);
last = i;
}
}
return list;
}
https://leetcode-cn.com/problems/merge-intervals/
注意考虑最后一对有没有存进去
public int[][] merge(int[][] intervals) {
List<int[]> res = new LinkedList<>();
Arrays.sort(intervals, (o1, o2) -> Integer.compare(o1[0], o2[0]));//按左边界从小到大排
show(intervals);
//每有一个左小于上一个右,合并
int l = intervals[0][0];//第一个左边界
int r = intervals[0][1];//第一个右边界
for(int i=1; i<intervals.length; i++) {
if(intervals[i][0] <= r ) {//如果左小于上一个右,l不变,r扩大
r = Math.max(intervals[i][1], r);//当前右和上一个右当中大的当新右
}else{//如果左大于上一个右,res.add,更新l r
res.add(new int[]{l, r});
l = intervals[i][0];//更新左边界
r = intervals[i][1];//更新右边界
}
}
res.add(new int[]{l, r});//最后一对没加,加一下
return res.toArray(new int[res.size()][]);
}
https://leetcode-cn.com/problems/monotone-increasing-digits/
/*
* 局部最优:遇到strNum[i - 1] > strNum[i]的情况,
* 让strNum[i - 1]--,然后strNum[i]给为9,可以保证这两位变成最大单调递增整数。
* 全局最优:得到小于等于N的最大单调递增的整数。
*
*/
public int monotoneIncreasingDigits(int n) {
if (n==0)return 0;
char[] chars= Integer.toString(n).toCharArray();
//start记录从第几位开始后面都是9了。一开始设置最大值是为了防止本身就是递增的
int start=Integer.MAX_VALUE;
for (int i=chars.length-1;i>0;i--){
if (chars[i]<chars[i-1]){
chars[i-1]--;
start=i;
}
}
StringBuilder res=new StringBuilder();
for (int i=0;i<chars.length;i++){
if (chars[i]=='0'&&i==0)continue;//防止出现09这样的情况
if (i>=start){//到了start的位置,后面全都搞成9
res.append('9');
}else res.append(chars[i]);//在start之前该是啥是啥
}
return Integer.parseInt(res.toString());
}
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/
public int maxProfit(int[] prices, int fee) {
int sum = 0;
int buy = prices[0] + fee;
for(int i=0; i<prices.length; i++) {
if( prices[i] + fee < buy) {//有更低的买入价,更新
buy = prices[i] + fee;
} else if(prices[i] > buy) {//如果我现在已经能赚钱了
sum += prices[i] - buy; //先把肯定能赚到的这一部分加上
buy = prices[i];
//这样接下来有两种情况
//1. 如果后面的价格比我卖出的buy高,我依然可以赚到这部分钱
//2. 如果后面的价格低了,有更低的价格买入价会更新的,我们赚了就跑了,完美
}
}
return sum;
}
https://leetcode-cn.com/problems/binary-tree-cameras/
/*
* 2. 该节点无覆盖0(无摄像),本节点有摄像头1,本节点有覆盖2(无摄像)。空结点当做有覆盖
* 后序遍历,从下往上,从叶子节点的爹开始安装摄像头
*/
public int count = 0;//记录摄像头个数
public int minCameraCover(TreeNode root) {
if( trval(root) == 0) count++; //如果答案是0,说明就一个根,答案++
return count;
}
private int trval(TreeNode root) {
// 1. 空节点,该节点有覆盖,返回2
if (root == null) {
System.out.println("当前结点为空,返回2");
return 2;
}
//后序遍历树
int l = trval(root.left);
int r = trval(root.right);
// 2. 左右节点都有覆盖无摄像,那当前点是无覆盖无摄像的
if (l == 2 && r == 2) {
System.out.println("当前结点" + root.val + "的左右孩子都为空,返回0");
return 0;
}
// 4. 左右有一个孩子没有覆盖,那就该装个摄像头了
// 注意第一次错在这了!!!左右有一个是0的优先级很高!!先判断!!!
if( l == 0 || r == 0) {
System.out.println("当前结点" + root.val + "的左右孩子有一个是0,返回1");
count++;
return 1;
}
// 3. 左右有一个有摄像,不用放摄像了,本结点被覆盖了
if( l == 1 || r == 1) {
System.out.println("当前结点" + root.val + "的左右孩子有一个是1,返回2");
return 2;
}
return -1;//走不到这的
}