一)概念
贪心算法(Greedy Alogorithm)又叫登山算法,它的根本思想是逐步到达山顶,即逐步获得最优解,是解决最优化问题时的一种简单但是适用范围有限的策略。
贪心算法没有固定的框架,算法设计的关键是贪婪策略的选择。贪心策略要无后向性,也就是说某状态以后的过程不会影响以前的状态,至于当前状态有关。
贪心算法是对某些求解最优解问题的最简单、最迅速的技术。某些问题的最优解可以通过一系列的最优的选择即贪心选择来达到。但局部最优并不总能获得整体最优解,但通常能获得近似最优解。
在每一步贪心选择中,只考虑当前对自己最有利的选择,而不去考虑在后面看来这种选择是否合理。
二)找出全局最优解的要求
在遇见问题时如何确定是否可以使用贪心算法解决问题,那么决定一个贪心算法是否能找到全局最优解的条件是什么呢?其实就是以下两点:
最优子结构(optimal subproblem structure,和动态规划中的是一个概念)
最优贪心选择属性(optimal greedy choice property)
三)求解时应考虑的问题
1.候选集合S
为了构造问题的解决方案,有一个候选集合C作为问题的可能解,问题的最终解均取自于候选集合C。
2.解集合S
随着贪心选择的进行,解集合不断扩展,直到构成一个满足问题的完整解。
3.解决函数solution
检查解集合是否构成问题的完整解。
4.选择函数select
即贪心策略,这是贪心算法的关键,它指出哪个候选对象有希望构成成问题的解。
5.可行函数feasible
检查解集合中加入一个候选对象是否可行,即解集合扩展后是否满足约束条件。
四)基本步骤
贪心算法使用基本步骤:
1.从问题的某个初始解出发
2.采用循环语句,当可以向求解目标前进一步时,就根据局部最优策略,得到一个不分解,缩小问题的范围或规模。
3.将所有的部分解综合起来,得到问题的最终解。
五)贪心策略选择
贪心算法的原理是通过局部最优来达到全局最优,采用的是逐步构造最优解的方法。在每个阶段,都做出一个看上去最优的,决策一旦做出,就不再更改。
要选出最优解可不是一件容易的事,要证明局部最优为全局最优,要进行数学证明,否则就不能说明为全局最优。
很多问题表面上看来用贪心算法可以找到最优解,实际上却把最优解给漏掉了。这就像现实生活中的“贪小便宜吃大亏”。所以我们在解决问题的时候,一定要谨慎使用贪心算法,一定要注意这个问题适不适合采用贪心算法。
————————————————
版权声明:本文为CSDN博主「Kk.巴扎嘿」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_49370884/article/details/126247776
用一个左右指针,不断计算容积,记录最大值,左右指针哪面小,哪面就进行移动。
public int maxArea(int[] height) {
//设置左右指针
int left = 0;
int right = height.length - 1;
int max = 0;
while (left <= right){
//计算容积,用max记录最大值
max = Math.max(max,(right - left) * Math.min(height[right],height[left]));
//哪边低哪边就动
if (height[left] <= height[right]){
left ++;
}else {
right --;
}
}
return max;
}
用labuladong的算法
如果i到j点无法到达,那么i到j点之间的点一定不能作为起点。
public static int canCompleteCircuit(int[] gas, int[] cost) {
//记录全局油量剩余
int sum = 0;
for (int i = 0; i < gas.length; i++) {
sum += gas[i] - cost[i];
}
if (sum < 0){
//全局不可能有解
return -1;
}
// 记录油箱中的油量
int tank = 0;
// 记录起点
int start = 0;
for (int i = 0; i < gas.length; i++) {
tank += gas[i] - cost[i];
if (tank < 0) {
// 无法从 start 到达 i + 1
// 所以站点 i + 1 应该是起点
tank = 0;
start = i + 1;
}
}
return start == gas.length ? 0 : start;
}
这里贪心理解不够到位,用一下别人的算法
if len_nums == 1:
return 1
direction = None
res = 0 # 统计变化次数
for i in range(1, len_nums):
# 统计变化次数
# 无变化
if nums[i] == nums[i-1]:
continue
# 有变化:升高了
elif nums[i] - nums[i-1] > 0:
# 如果上一次也是升高,不要算进去,因为其实不是摆动
if direction == 1:
continue
direction = 1
res += 1
# 有变化:降低了
else:
# 如果上一次也是降低,不要算进去,因为其实不是摆动
if direction == 0:
continue
direction = 0
res += 1
return res+1 # 因为统计的是变化的次数,最终的结果是序列的长度,所以需要+1.
public static int wiggleMaxLength(int[] nums) {
if (nums.length == 1)
return 1;
int flag;
int cnt;
if (nums[1] - nums[0] > 0){
cnt = 2;
flag = 1;
} else if (nums[1] == nums[0]) {
cnt = 1;
flag = 0;
}else {
cnt = 2;
flag = -1;
}
for (int i = 1; i < nums.length - 1; i++) {
if (nums[i] == nums[i + 1]){
//相等就直接跳过
continue;
}else if (nums[i] < nums[i + 1]){
//开始增
if (flag == 1){
//前一个是增,那么就不加入
continue;
}else {
cnt++;
flag = 1;
}
}else {
//开始减
if (flag == -1){
//前一个是减,不加入
continue;
}else {
cnt++;
flag = -1;
}
}
}
return cnt;
}