基本算法思想(2)了解贪心算法

  • 在算法的大家庭中,贪心算法是相对比较容易理解的算法,它只保证局部最优,进而达到全局最优。但其苛刻的使用条件让我们只闻其声不见其人,接下来让我们通过这篇文章更加深刻的了解贪心算法。

🎆贪心法

  • 定义:贪心法又被称为贪婪算法,从某一点开始,就是在每一个解决问题步骤中使用贪心原则,采取当前状态下最有利或最优化的选择,不断改进该解答,持续在每一步骤中选择最佳的方法。
  • 思想:贪心算法总是作出当前看来最好的选择。所以它不会从整体最优考虑,而是从局部。例如你在寻找最短路径时,在当前结点选择最短路径就是运用了贪心算法。但这不能保证这条路以及之后的选择加起来就是最短路径。
  • 在下图中,如果你要找从5到3的最短路径,那么我们一眼就能看出直接从5到3是最短的。但是如果你在这使用贪心算法,那么选择当前最优,你的移动路径就会变成5->1->2->3。很明显这边无法使用贪心算法。
    在这里插入图片描述

🎆优与劣

  • 刚才通过一张图我们简单的了解了一下贪心算法的运行过程。所以如此剑走偏锋的贪心算法,我们应当在什么情况下使用它呢?
  • 贪心算法的基本步骤为
    步骤1:从某一起点出发。
    步骤2:在每一个解决问题步骤中,根据局部最优策略,得到一部分解,缩小问题规模。
    步骤3:将所有解综合起来。
    不难看出,在以上的过程中,由于贪心算法的策略都是当前最优解,很显然高效和简介的代码(自顶向下利用迭代拆解问题)是它的最大优点。
  • 贪心算法的缺点
    很显然你最终得到的结果不一定是最终的答案以及最优解。这一点在我们前面运用最短路径已经进行了讲解。

🎇贪心算法的应用范围

  • 贪心算法经常用于求图的最小生成树(一种数据结构),最短路径,霍哈夫曼编码。
  • 如何去应用贪心算法呢。贪心算法在某种程度上算是动态规划算法的特例,但如果在使用动态规化后发现问题超时,就可以考虑使用贪心算法。
    接下来我们用代码来介绍几种贪心算法的经典例子。

✨贪心法教你数钱——钱币找零问题

  • 钱币找零问题是一个简单的问题,但也能很直观的体现出贪心算法的逻辑理念和思想。

  • 题目:假设有1元,2元,5元,10元,20元,50元的纸币6种,现在要用这些钱来找零。如何让纸币的数量最少?
    在这里插入图片描述

  • 透过问题,我们很快就能凭借聪明的大脑知道每次要进一步使用面值大的纸币即可。

  • 👇下面用代码实现


#include<stdio.h>
int main(void) {
	int value[6] = { 1,2,5,10,20,50 };
	int price,pay;
	printf("您买的东西价格为:");
	scanf("%d", &price);
	printf("您付款了:");
	scanf("%d", &pay);
	if (price > pay) {
		printf("抱歉,您的钱不够");
		return 0;
	}
	int change = pay-price;//找零应该找的钱
	printf("应当找您:%d元\n",change);
	int nums=0;//总张数
	int num[6] = { 0 };//不同面值分别使用的张数
	int i;
	for (i = 5;i >= 0;i--) {
		while(value[i] <= change) {
			num[i]++;
			change -= value[i];
			nums++;
		}
	}
	printf("纸币总共:%d张\n", nums);
	printf("1元纸币%d张,2元纸币%d张,5元纸币%d张,10元纸币%d张,20元纸币%d张,50元纸币%d张", num[0], num[1], num[2], num[3], num[4], num[5]);
}
  • 当然我们也可以加深难度,控制店内能找零的纸币个数,来影响最终的答案。但当然我们还是可以用贪心算法去完美地解决这个问题。

✨贪心法-——脑子正常人玩不出来的跳跃游戏(LeetCode55)

在这里插入图片描述

  • 倒序遍历是解决这道题目的主要思想之一,检查前面一个点能否跳到当前这个点,然后逐步往前推。
  • 而这边的贪心思想体现在每个位置能够到达的最远距离,所以就有nums[i]+i的判断语句,用最远距离来推断是否在这之中。
  • 代码块如下:
bool canJump(int* nums, int numsSize){
     if(nums==NULL||numsSize==0)return false;//判断数组是否为空
     int end=numsSize-1;
     for(int i=end-1;i>=0;i--){//从尾往前遍历
         if(i+nums[i]>=end){
             end=i;//拿第一次举例:end在下标为4的最后一个位置上。而i在他们前面一个,这里判断是否能从这个往前跳,如果可以则继续。 
         }//当这一次不满足时,就继续看前面的。
     }
     return end==0;//end每次改变代表这有一个点能跳到end当前这个点,所以依次往前能推回numsSize-1
}

  • 跳够了没?如果没有跳够的话,我们还有跳跃游戏的升级版。
  • 在上一题的情况下,我们还对跳跃次数进行了要求,你知道如何解决这个问题么?

请添加图片描述

  • 在这一题中已经假设了我们总是能够到达数组的最后一个位置。也就是说我们只需要在前面判断如何跳的更远。但是我们不能直接运用贪心算法,啥都不去想,如果你直接运用。那么在例一的第一个点(下标0)你就会做错选择,跳到下标为2的地方。

  • 而经过我们细心的分析可以发现,它比较的是跳跃后下一个跳跃的距离,找出最远距离。例如 [ 2 , 3 , 1 , 1 , 4 ], 比较的是下标1能跳到的最远距离nums [ 1 ] + 1 和下标2 能跳到的最远距离 nums [ 2 ] + 2的大小。

  • 具体代码如下:

int jump(int* nums, int numsSize){
    int i=0,j=0,flag=0,max=0,t=0;//flag用来对跳跃次数计数,max在存放的是比较的距离中较大的,而t是它的下标。
    if(numsSize==1)return 0;//这里的两个判断是特殊条件,如果为【1】【0】这种,则直接当前位置
    if(i+nums[i]==numsSize-1)return 1;//直接能从第一点跳到最后一点的类型。
     while(i+nums[i]<=numsSize-1){//判断当前位置能否跳到最后一点
         t=i+1;
         max=nums[i+1]+i+1;//能跳到的第一个(如果是0也没事,for循环的判断会让它结束)
        for(j=i+1;j<=i+nums[i];j++){//这里就是对能跳到的地方 他们的距离进行比较,标记max和n
             if(nums[j]+j>max){
                 max=nums[j]+j;
                 t=j;
             }
        }
        flag++;
        if(max>=numsSize-1)break;//这一步是防止下标越界,因为j<=i+nums[i],所以如果i+nums[i]刚好到这已经超过numsSize时,t就会越界!
        i=t;
        }
    flag++;//因为最后一遍判断的时候,没有对那次跳跃++,所以在结尾++
    return flag;
}

🥇写在结尾

  • 根据上文的研究,我们了解了贪心算法的优点和缺点,也做了和贪心算法有关的应用。贪心算法作为算法家族的特殊存在,如何合理的应用它的思想,来为我们解决问题,是我们算法能力进步的重要以一步。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值