贪心算法

贪心算法

一、定义

贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,他所做出的是在某种意义上的局部最优解。

贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择,选择的贪心策略必须具备无后效性,即某个状态以前的过程不会影响以后的状态,只与当前状态有关。
贪心策略适用的前提是:局部最优策略能导致产生全局最优解。

二、LeetCode相关题目

1、Leetcode_455. 分发饼干

题目描述:

假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有一个尺寸 sj 。如果 sj >= gi ,我们可以将这个饼干 j 分配给孩子 i ,这个孩子会得到满足。你的目标是尽可能满足越多数量的孩子,并输出这个最大数值。

注意:

你可以假设胃口值为正。
一个小朋友最多只能拥有一块饼干。

示例 1:

输入: [1,2,3], [1,1]

输出: 1

解释:
你有三个孩子和两块小饼干,3个孩子的胃口值分别是:1,2,3。
虽然你有两块小饼干,由于他们的尺寸都是1,你只能让胃口值是1的孩子满足。
所以你应该输出1。

示例 2:

输入: [1,2], [1,2,3]

输出: 2

解释:
你有两个孩子和三块小饼干,2个孩子的胃口值分别是1,2。
你拥有的饼干数量和尺寸都足以让所有孩子满足。
所以你应该输出2.

题解:

class Solution {
    public int findContentChildren(int[] g, int[] s) {
        // 首先对两个数组排序
        Arrays.sort(g);
        Arrays.sort(s);
        // 两个数组的下标索引
        int gi = g.length - 1;
        int si = s.length - 1;
        // 满足孩子的数量
        int result = 0;
        // 分发饼干
        while (gi >= 0 && si >= 0) {
            // 将最大的饼干分给胃口最大的小孩 孩子满足
            if (g[gi] <= s[si]) {
                // 满足孩子数量+1
                result++;
                // 孩子胃口数下标-1
                gi--;
                // 饼干数组下标-1
                si--;
            } else {
                // 将最大的饼干分给胃口最大的小孩 孩子不满足
                // 将饼干分给胃口小一号的孩子
                gi--;
            }
        }
        return result;
    }
}

2、Leetcode_392. 判断子序列

题目描述:

给定字符串 s 和 t ,判断 s 是否为 t 的子序列。

你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。

字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。

示例 1:
s = “abc”, t = “ahbgdc”

返回 true.

示例 2:
s = “axc”, t = “ahbgdc”

返回 false.

后续挑战 :

如果有大量输入的 S,称作S1, S2, … , Sk 其中 k >= 10亿,你需要依次检查它们是否为 T 的子序列。在这种情况下,你会怎样改变代码?

致谢:

特别感谢 @pbrother 添加此问题并且创建所有测试用例。

题解:

class Solution {
    public boolean isSubsequence(String s, String t) {
        // 起始索引
        int index = -1;
        for (char c : s.toCharArray()) {
            // 查询字符在不在字符串t中
            index = t.indexOf(c, index + 1);
            // 查询不到
            if (index == -1) {
                return false;
            }
        }
        return true;
    }
}

3、Leetcode_435. 无重叠区间

题目描述:

给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。

注意:

可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。

示例 1:

输入: [ [1,2], [2,3], [3,4], [1,3] ]

输出: 1

解释: 移除 [1,3] 后,剩下的区间没有重叠。

示例 2:

输入: [ [1,2], [1,2], [1,2] ]

输出: 2

解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。

示例 3:

输入: [ [1,2], [2,3] ]

输出: 0

解释: 你不需要移除任何区间,因为它们已经是无重叠的了。

题解

 class Solution {
    public int eraseOverlapIntervals(int[][] intervals) {
        // 特殊情况处理
        if (intervals.length == 0) {
            return 0;
        }
        // 先排序 以区间末端从小到大排序
        Arrays.sort(intervals, new Comparator() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[1] - o2[1];
            }
        });
        // 最多能组成的不重叠区间个数
        int count = 1;
        // 前一区间的末端位置
        int end = intervals[0][1];
        for (int i = 0; i < intervals.length; i++) {
            // 两个区间有重叠
            if (intervals[i][0] < end) {
                continue;
            }
            // 两个区间无重叠 修正前一区间末端位置
            end = intervals[i][1];
            // 不重叠区间数+1
            count++;
        }
        // 总区间数 - 不重叠区间数 = 要删去的区间数
        return intervals.length - count;
    }
}

4、Leetcode_665. 非递减数列

题目描述:

给定一个长度为 n 的整数数组,你的任务是判断在最多改变 1 个元素的情况下,该数组能否变成一个非递减数列。

我们是这样定义一个非递减数列的: 对于数组中所有的i (1 <= i < n),满足 array[i] <= array[i + 1]。

示例 1:

输入: [4,2,3]
输出: True
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

示例 2:

输入: [4,2,1]
输出: False
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

说明: n 的范围为 [1, 10,000]。

题解:

class Solution {
    public boolean checkPossibility(int[] nums) {
        int length = nums.length;
        int count = 0;
        for (int i = 1; i < length && count < 2; i++) {
            if (nums[i] >= nums[i - 1]) {
                continue;
            }
            count++;
            if (i >= 2 && nums[i - 2] > nums[i]) {
                // 是当前数字等于前一个数字
                nums[i] = nums[i - 1];
            } else {
                // 使前一个数字小于当前数字
                nums[i - 1] = Integer.MIN_VALUE;
            }
        }
        return count <= 1;
    }
}

5、Leetcode_1046. 最后一块石头的重量

题目描述:

有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块最重的石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果x == y,那么两块石头都会被完全粉碎;
如果x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为y-x。

最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

提示:
1 <= stones.length <= 30
1 <= stones[i] <= 1000

解答:

class Solution1406 {
    public int lastStoneWeight(int[] stones) {
        // 末尾下标
        int end = stones.length - 1;
        // 已处理的石头数量
        int count = 0;
        while (count != stones.length && count != end) {
            // 先排序
            Arrays.sort(stones);
            int x = stones[end - 1];
            int y = stones[end];
            //x == y,那么两块石头都会被完全粉碎;
            if (x == y) {
                // 粉碎的石头标记为-1
                stones[end - 1] = -1;
                stones[end] = -1;
                // 已处理的石头+2
                count += 2;
            } else {
                stones[end - 1] = stones[end] - stones[end - 1];
                // 粉碎的石头标记为-1
                stones[end] = -1;
                // 已处理的石头+2
                count += 1;
            }
        }
        Arrays.sort(stones);
        // count == stones.length 表示全部处理完返回0 否则剩下一个未处理 返回stones[end]
        return count == stones.length ? 0 : stones[end];
    }
}

你可能感兴趣的:(算法与数据结构)