贪心思想
贪心思想
保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。
以下题为leetcode原题。
1. 分配饼干
思路描述:
首先对孩子和饼干进行从小到大排序,那么依次比较,
如果饼干大于孩子的胃,则加1,
否则指向下一块饼干。
此方法也可称为双指针法。
代码实现:
public static int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int num = 0;
int i = 0, j = 0;
while (i < g.length && j < s.length) {
//此处如果饼干大于孩子的胃,则num+1 同时, i j同时加1
if (s[j] >= g[i]) {
++num;
i++;
}
//此处 if不成立时候,只需 指向饼干的指针指向下一块饼干
j++;
}
return num;
}
2. 无重叠区间
435. Non-overlapping Intervals (Medium)
思路描述:
题目求的是找到需要移除区间的最小数量,使剩余区间互不重叠;
则求出最多有多少区间互不重叠,总的区间数-最多区间不重叠=移除的最小区间数量;
找出能存放的最大区间个数,则区间的末尾非常重要,选择的区间末尾越小,
留给后面的区间的空间越大,那么后面能够选择的区间个数也就越大。
首先对区间的末尾进行排序,然后每次选择末尾最小,并且和前一个区间不重叠的区间
代码实现:
public int eraseOverlapIntervals(int[][] intervals) {
//需要添加判断,当数组个数为0时,返回0
if (intervals.length == 0) {
return 0;
}
//对二维数组根据行中的值排序排序
Sort(intervals);
int num = 0;
int end = intervals[0][1];
for (int i = 1; i < intervals.length; i++) {
if (intervals[i][0] >= end) {
//符合条件 ++
num++;
//end进行替换
end = intervals[i][1];
}
}
//至少存在一个
num++;
return intervals.length - num;
}
/**
* 对二维数组根据第二列排序
* @param arr
*/
private void Sort(int[][] arr) {
Arrays.sort(arr, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});
//lambda表达式
// Arrays.sort(arr,((o1, o2) -> o1[1]-o2[1]));
}
3. 投飞镖刺破气球
452. Minimum Number of Arrows to Burst Balloons (Medium)
思路描述:
将题转化为
求解区间最多不重合区间个数(此时如果区间的端点相等也算重合),
即是求所需的最小弓箭数
代码实现:
/**
* 思路 :将题转化为 求解区间最多不重合区间个数(此时如果区间的端点相等也算重合),即是求所需的最小弓箭数
*
* @param points
* @return
*/
public static int findMinArrowShots(int[][] points) {
//如果没有区间,则弓箭数为0
if (points.length == 0) {
return 0;
}
sort(points);
int num = 1;
int end = points[0][1];
for (int i = 1; i < points.length; i++) {
if (end < points[i][0]) {
//符合条件++
num++;
//此时符合条件,进行替换,一定是符合条件时执行该语句
end = points[i][1];
}
}
return num;
}
private static void sort(int[][] arr) {
Arrays.sort(arr, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[1] - o2[1];
}
});
}
4. 根据身高和序号重组队列
思路描述:
* 思路: 这道题 本身并没有思路 参考了 leetcode 上面的解析后,有点理解
* <p>
* 首先按照H高度降序(这是让高个字先排序),再按照K升序(根据K找位置),
* 排序结束后,再按照K来插入,因为先排的高个子,所以高个子排完序后,低个子插入并不会影响它的相对位置。
* 所以根据K来插入元素。
*
* 1.排序规则:按照H高度降序排序,K个数升序排序
* 2.遍历后的数组,根据K插入到K的位置上
代码实现:
public int[][] reconstructQueue(int[][] people) {
// [7,0], [7,1], [6,1], [5,0], [5,2], [4,4]
// 再一个一个插入。
// [7,0]
// [7,0], [7,1]
// [7,0], [6,1], [7,1]
// [5,0], [7,0], [6,1], [7,1]
// [5,0], [7,0], [5,2], [6,1], [7,1]
// [5,0], [7,0], [5,2], [6,1], [4,4], [7,1]
//先按照 H降序 K升序进行排序
Arrays.sort(people, new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0];
}
});
List<int[]> list=new LinkedList<>();
//再根据K 对数组进行插入
for (int[] person : people) {
list.add(person[1],person);
}
//将一个存放一维数组的list转化为一个二维数组
// return list.toArray(new int[list.size()][2]);
//此处传入people更加方便,速度会变慢
return list.toArray(people);
}
5. 买卖股票最大的收益
121. Best Time to Buy and Sell Stock (Easy)
思路描述:
方法一: 暴力法
方法二:优化过
首先用数组中的第一个元素作为min,遍历数组,如果数组元素小于min,则进行替换,
否则,计算此元素和min的差值是否大于max,如果大于则进行替换,否则继续执行
代码实现:
暴力法:
public static int maxProfit(int[] prices) {
int max=0;
for (int i = 0; i < prices.length-1; i++) {
for (int j = i+1; j < prices.length; j++) {
if (prices[j]-prices[i]>max){
max=prices[j]-prices[i];
}
}
}
return max;
}
优化后算法:
public static int maxProfit3(int[] prices){
if (prices.length<=0)
return 0;
//第一个元素作为min
int min=prices[0];
int max=0;
//遍历元素
for (int i = 1; i < prices.length; i++) {
//如果数组元素比min小,则替换,否则,进行运算 找出max.
if(prices[i]<=min){
min=prices[i];
}else {
max=Math.max(prices[i]-min,max);
}
}
return max;
}
6. 买卖股票最大的收益2
122. Best Time to Buy and Sell Stock II (Easy)
思路描述:
* 对于 [a, b, c, d],如果有 a <= b <= c <= d ,
* 那么最大收益为 d - a。而 d - a = (d - c) + (c - b) + (b - a) ,
* 因此当访问到一个 prices[i] 且 prices[i] - prices[i-1] > 0,
* 那么就把 prices[i] - prices[i-1] 添加到收益中。
代码实现:
private static int maxProfit4(int[] prices) {
//初始利润为0
int profit=0;
//从1遍历数组
for (int i = 1; i < prices.length; i++) {
int num=prices[i]-prices[i-1];
//如果相邻两次差值大于0,则加入到利润中。 根据上述规律。
if(num>0){
profit+=num;
}
}
return profit;
}
7. 种植花朵
思路描述:
从1 到 arr.length-1遍历,
如果第一个则判断当前和下一个是否有花,没有花,则flowerbed[i]=1(种花),num++
如果是末尾则判断当前和上一个是否有花,没有花,则flowerbed[i]=1(种花),num++
如果是中间位置,则判断当前、上一个和下一个是否同时有花,没有花,则flowerbed[i]=1(种花),num++
代码实现:
public boolean canPlaceFlowers(int[] flowerbed, int n) {
int num=0;
if(flowerbed.length==1){
if(flowerbed[0]==0){
num=1;
}
return num>=n;
}
//遍历
for (int i = 0; i < flowerbed.length; i++) {
//判断第一个花盆
if(i==0){
if(flowerbed[i]==0 && flowerbed[i+1]==0){
num++;
flowerbed[i]=1;
}
}else if (i==flowerbed.length-1){ //判断最后一个花盆
if(flowerbed[i]==0 && flowerbed[i-1]==0){
num++;
flowerbed[i]=1;
}
}else { //判断中间花盆
if(flowerbed[i]==0 && flowerbed[i-1]==0 && flowerbed[i+1]==0){
num++;
flowerbed[i]=1;
}
}
}
//是否满足要求
return num>=n;
}
8. 判断是否是子序列
思路:
也可称为此方法为双指针法
两个指针分别指向s,t
如果指向的字符相等则同时指向下一个字符,指针均+1
否则,只有长字符串的指针加1,
循环结束条件是 两个指针有任意一个指向末尾。
return 短指针>=短字符串的长度
代码实现:
public static boolean isSubsequence(String s, String t) {
//i指向长的,j指向短的
int i=0,j=0;
while (i<t.length() && j<s.length()){
if(t.charAt(i)==s.charAt(j)){
j++;
}
i++;
}
return j>=s.length();
}
9. 子数组最大的和
思路描述:
本题参考leetcode题解,大神解法
首先对数组遍历,当前最大连续子序列和为sum,结果为ans
if sum>0 则 sum对结果有增益效果,则sum保留并加上当前遍历数字
if sum<0 则 sum对结果无增益效果,需要舍弃,则sum直接更新为当前遍历数字
每次比较sum和ans的代销,将最大值置为ans,遍历结束返回结果
代码实现:
public int maxSubArray2(int[] nums) {
//存放最终结果
int ans=nums[0];
//存放最大连续子序列的和
int sum=0;
for (int num : nums) {
//sum对结果有增益效果,加上
if(sum>0){
sum+=num;
}else {
//sum对结果无增益效果,果断舍弃,更新为当前遍历数字
sum=num;
}
//比较两者的最大值,赋值给ans
ans=Math.max(ans,sum);
}
return ans;
}
DP法:
public int maxSubArray(int[] nums) {
int[] dp = new int[nums.length];
dp[0] = nums[0];
int max = nums[0];
for (int i = 1; i < nums.length; i++) {
//此处判断dp[i-1]+nums[i]和nums[i]的较大值,这样才能比较出连续和
dp[i] = Math.max(dp[i- 1] + nums[i], nums[i]);
if (max < dp[i]) {
max = dp[i];
}
}
return max;
}