文章目录
题目都是Leetcode上,是基于谷歌大佬整理的题目,可以在github上搜索Leetcode101.
1.分配问题
455. 分发饼干–简单
给定一堆饼干和一群孩子,求这些饼干可以使得多少个孩子吃饱,每个孩子只能吃一块饼干。
Input: [1,2], [1,2,3]
Output: 2
第一个数组表示孩子的饥饿度,第二个数组表示饼干的大小,这里只需要把大小为 [1,2] 的饼干分给饥饿度为[1,2]的孩子即可。
/**
思路:优先让最小的饼干,满足饥饿度最小的孩子。
**/
class Solution {
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int child = 0, cookie = 0;
while (child < g.length && cookie < s.length) {
if (g[child] <= s[cookie]) child++;
cookie++;
}
return child;
}
}
135. 分发糖果–困难
n个站成一排的孩子,每个孩子有一个评分,相邻孩子中评分高的比其相邻孩子的糖果更多,求如何分配使得分发糖果最少。(每个孩子最少一颗糖果)
输入:ratings = [1,0,2]
输出:5
分配: 2 1 2, 即给评分0的孩子分配一个,其余孩子分配2个
/**
思路:两次遍历,从左往右和从右往左遍历,如果评分高,则在其相邻孩子的基础上+1
**/
class Solution {
public int candy(int[] ratings) {
int size = ratings.length;
int[] value = new int[size];
Arrays.fill(value, 1);
for (int i = 1; i < ratings.length; i++) {
if (ratings[i] > ratings[i-1]) {
value[i] = value[i-1] + 1;
}
}
for (int i = ratings.length-2; i >= 0; i--) {
if (ratings[i] > ratings[i+1]) {
if (value[i] < (value[i+1] + 1))
value[i] = value[i+1] + 1;
}
}
return Arrays.stream(value).sum();
}
}
435. 无重叠区间–中等
给定一个区间集,求移除最少的区间使得区间互相不重合。(边界相等不算重合)
输入: intervals = [[1,2],[2,3],[3,4],[1,3]]
输出: 1
移除 [1,3] 区间,使得剩余区间不重合
/**
思路:按照区间右端点升序排序,右端点越小,其留出的空间越大。
**/
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals, (e1,e2) -> e1[1] - e2[1]);
int count = 0;
int right = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] >= right) {
right = intervals[i][1];
} else {
count++;
}
}
return count;
}
}
/**
动态规划思路:会超时。
类似于最长递增子序列, dp[i] = Math.max(dp[i], dp[j] + 1); 如果i可以放在j之后
**/
class Solution {
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals, (e1, e2) -> {return e1[0] - e2[0];});
int[] dp = new int[intervals.length];
Arrays.fill(dp, 1);
for (int i = 1; i < intervals.length; ++i) {
for (int j = 0; j < i; ++j) {
if (intervals[i][0] >= intervals[j][1]) dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
return intervals.length - Arrays.stream(dp).max().getAsInt();
}
}
605. 种花问题–简单
给定一个01串,0表示可以种花,1表示已被占用了,要求相邻两块空地不能同时种花,判断给定花朵数是否可种植。
输入:flowerbed = [1,0,0,0,1], n = 1
输出:true
可以在中间的0种花。
/**
思路:计算给定的花园能种几棵花,然后判断其与目标的大小。
处理边界:0的左为0 和 flowerbed.length-1 的右为0
**/
class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {
int sum = 0;
for (int i = 0; i < flowerbed.length; ++i) {
if (flowerbed[i] == 1) continue;
int l = i==0?0:flowerbed[i-1];
int r = (i==flowerbed.length-1)?0:flowerbed[i+1];
if(l == 0 && r == 0) {
flowerbed[i] = 1;
++sum;
}
}
return sum >= n;
}
}
452. 用最少数量的箭引爆气球–中等
给定一些气球的左右边界,一只箭可以垂直x方向射出,求用最少的箭扎破所有气球(擦边也算)。
输入:points = [[10,16],[2,8],[1,6],[7,12]]
输出:2
在 x=6 x=11处,用两个箭即可
/**
思路: 按照气球右端点升序排序。
第一个气球(即:最小的右端点必然需要一个气球),如果后面存在与之交集,则可以通过一个气球解决。
Integer.compare(e1[1], e2[1]); // 处理可能越界问题
**/
class Solution {
public int findMinArrowShots(int[][] points) {
Arrays.sort(points, (e1, e2) -> {
return Integer.compare(e1[1], e2[1]);
});
int sum = 1, right = points[0][1];
for (int i = 1; i < points.length; ++i) {
if (points[i][0] <= right) continue;
++sum;
right = points[i][1];
}
return sum;
}
}
763. 划分字母区间–中等
一个字符串划分成尽可能多的片段,使得相同字母出现在同一片段中,返回每个片段长度。
输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
划分结果为:"ababcbaca", "defegde", "hijhklij"
/**
思路:在遍历前,记录每个字符的最后一次出现位置。
遍历时,start记录每个片段的开始,end记录开始到遍历之间所有字符的最后一次出现位置,
如果 i == end,说明 (start, end)之间包含了里面的字母,在其区间外面不包含。
**/
class Solution {
public List<Integer> partitionLabels(String s) {
List<Integer> res = new ArrayList<>();
Map<Character, Integer> map = new HashMap<>();
for (int i = 0; i < s.length(); ++i) map.put(s.charAt(i), i);
int start = 0, end = map.get(s.charAt(0));
for (int i = 0; i < s.length(); ++i) {
char cur = s.charAt(i);
end = Math.max(end, map.get(cur));
if (i < end) continue;
res.add(end - start + 1);
start = i + 1;
}
return res;
}
}
122. 买卖股票的最佳时机 II–中等
每次可以买卖一只股票,且每次只能持有一只股票,求如何买卖获得的利润最大。
输入:prices = [7,1,5,3,6,4]
输出:7
第2天买,第3天卖 利润 5-1=4 第4天买第5天卖 6-3=3 总利润 4+3=7
/**
思路:贪心
只要涨了,就加上该利润。
**/
class Solution {
public int maxProfit(int[] prices) {
int profit = 0;
for (int i = 1; i < prices.length; ++i) {
if (prices[i] > prices[i-1]) profit += prices[i] - prices[i-1];
}
return profit;
}
}
/**
思路:动态规划
dp[i][0]: 表示当前有一只股票
dp[i][1]: 表示当前没有股票
那么 初始 dp[i][0] = -prices[0];
**/
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length + 1][2];
dp[1][0] = -prices[0];
for (int i = 1; i < prices.length; ++i) {
int cur = i+1;
dp[cur][0] = Math.max(dp[i][0], dp[i][1] - prices[i]);
dp[cur][1] = Math.max(dp[i][1], dp[i][0] + prices[i]);
}
return dp[prices.length][1];
}
}
406. 根据身高重建队列–中等
给定一个二维数组,其中 p e o p l e [ i ] [ 0 ] people[i][0] people[i][0]表示第i个人身高, p e o p l e [ i ] [ 1 ] people[i][1] people[i][1]表示他前面应该有几个人身高大于等于他,返回构建后的队列。
输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
/** 两种思路图示:如下图
思路:先降排,后插入。
按照身高降序,按照位序升序。
也就是说 [7,0] 在 [7,1] 之前。
**/
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, (e1, e2) -> {
if(e1[0] != e2[0]) return e2[0] - e1[0];
else return e1[1] - e2[1];
});
List<int[]> res = new ArrayList<>();
for (int i = 0; i < people.length; ++i) {
res.add(people[i][1], people[i]);
}
return res.toArray(new int[people.length][2]);
}
}
/**
思路2:先升序,后插入
**/
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, (e1, e2) -> {
if (e1[0] != e2[0]) return e1[0] - e2[0];
else return e2[1] - e1[1];
});
int[][] res = new int[people.length][];
for (int i = 0; i < people.length; ++i) {
int spaces = people[i][1] + 1;
for (int j = 0; j < people.length; ++j) {
if (res[j] == null) {
--spaces;
if (spaces == 0) {
res[j] = people[i];
break;
}
}
}
}
return res;
}
}
665. 非递减数列–中等
判断是否修改数组中的值,不超过一次,使得数组单调不减。
输入: nums = [4,2,3]
输出: true
修改 4 -> 1 数组递增
/**
分析:
存在以下两种情况:可以修改一次得到单调不减的序列
1. [1 6 4 7] 可以通过修改6 变成 1 2 4 7 序列
2. [5 6 4 7] 可以修改 4 变成 5 6 6 7 序列
结论 第一种情况是 4 > 1 可以修改一次 (即: 修改6)
第2中情况是 6 < 7 修改一次 (即:修改4)
当出现递减序列不满足这两种情况,则至少需要两次修改操作。
**/
class Solution {
public boolean checkPossibility(int[] nums) {
int cnt = 0;
for (int i = 1; i < nums.length; ++i) {
if(nums[i] >= nums[i-1]) continue;
int l = i==1?0:nums[i-2];
int r = i==nums.length-1?(1<<20):nums[i+1];
if (nums[i] >= l) ++cnt;
else if (nums[i-1] <= r) ++cnt;
else return false;
}
return cnt <= 1;
}
}