贪心思想

贪心思想

保证每次操作都是局部最优的,并且最后得到的结果是全局最优的。

以下题为leetcode原题。

1. 分配饼干

455. Assign Cookies (Easy)

思路描述:

首先对孩子和饼干进行从小到大排序,那么依次比较,
如果饼干大于孩子的胃,则加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. 种植花朵

605. Can Place Flowers (Easy)

思路描述:

从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. 子数组最大的和

53. Maximum Subarray (Easy)

思路描述:

本题参考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;
}
posted @ 2019-11-28 10:03  胖墩哥  阅读(310)  评论(0编辑  收藏  举报