贪心思想
保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。
以下题为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; }