贪心算法

Posted 思维的矩阵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了贪心算法相关的知识,希望对你有一定的参考价值。

一、贪心算法概念

贪心算法(英语:greedy algorithm),又称贪婪算法,是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是最好或最优的算法。

贪心算法在有最优子结构的问题中尤为有效。最优子结构的意思是局部最优解能决定全局最优解。简单地说,问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。

贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。

贪心法可以解决一些最优化问题,如:求图中的最小生成树、求哈夫曼编码……对于其他问题,贪心法一般不能得到我们所要求的答案。一旦一个问题可以通过贪心法来解决,那么贪心法一般是解决这个问题的最好办法。由于贪心法的高效性以及其所求得的答案比较接近最优结果,贪心法也可以用作辅助算法或者直接解决一些要求结果不特别精确的问题。在不同情况,选择最优的解,可能会导致辛普森悖论(Simpson's Paradox),不一定出现最优的解。

二、例题

2.1 去重字符串

给你一个字符串 s ,请你去除字符串中重复的字母,使得每个字母只出现一次。需保证 返回结果的字典序最小(要求不能打乱其他字符的相对位置)。

示例 1:
输入:s = "bcabc"
输出:"abc"

示例 2:
输入:s = "cbacdcbc"
输出:"acdb"

思考

题目有两个要求:

  1. 每个字母只出现一次

  2. 不能打乱其字符相对位置

  3. 返回结果字典序最小

每个字母只是出现一次,可以考虑使用hash来去重即可,但是题目又要求字典序最小且不能打乱字符相对位置,那么就是字典序越小尽量往前放。

通过栈这种数据结构可以满足条件1和条件2,但是字符相对位置,那么就是需要用再进栈的字母和栈顶进行比较,如果小于栈顶且后续还有栈顶一样的字母,那么就尅弹出,把小的字母入栈,反正弹出的字母还能继续入栈。

具体代码实现如下:

private String removeDuplicateLettersAction(String s) {
// 如果长度<=1 直接返回
if (s.length() <= 1) {
return s;
}
// 由于是ASCII,256 够用了
int[] counter = new int[256];
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
counter[c]++;
}
// 用栈来存字母,保证先后顺序
Stack<Character> stack = new Stack<>();
boolean[] hashMap = new boolean[256];
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
// 计数需要减一
counter[c]--;
// 包含直接 过
if (hashMap[c]) {
continue;
}
// 从栈顶找,如果栈顶大于当前字母且后续还有当前字母,直接弹出
while (!stack.isEmpty() && stack.peek() > c) {
if (counter[stack.peek()] == 0) {
break;
}
hashMap[stack.pop()] = false;
}
// 入栈新的字母,且记录包含该字母
stack.push(c);
hashMap[c] = true;
}
// 栈转字符串
StringBuilder sb = new StringBuilder();
while (!stack.isEmpty()) {
sb.append(stack.pop());
}
// 需要翻转字符串,输出合理的顺序
return sb.reverse().toString();
}

以上题目用了栈+贪心的思路,来解决问题


以上是关于贪心算法的主要内容,如果未能解决你的问题,请参考以下文章

贪心算法----区间覆盖问题(POJ2376)

Contig|scaffold|N50|L50|NG50|贪心算法|de bruiji graph|

贪心算法学习手册开放下载!!

贪心算法(各种贪心题目)

贪心算法-第一节:贪心算法概述

Matlab-贪心/贪婪算法