基础算法之贪心法、二分法及其他算法思想和技巧

1. 贪心法

贪心算法是求解一类最优化问题的方法。它总是考虑在当前状态下局部最优解的策略,使全局的结果达到最优。构造贪心策略并不困难,但是严谨的使用贪心算法来求解最优化问题需要对采取的策略进行证明。下面简单通过几个程序感受一下贪心算法。

1.1 简单贪心

PAT B1020月饼
题目描述:给定所有种类月饼的库存量、总售价、以及市场的最大需求量,请计算可以获得的最大收益是多少。(销售时允许取出一部分库存)
输入:库存和总售价
输出:最大收益(精确到小数点后两位)
代码截图:
在这里插入图片描述
在这里插入图片描述
思考:

  • 书中定义结构体时所有成员均使用double类型,为什么?
    可以看到代码第28行在计算price时,存在一个强制类型转换,因为price是一个double类型,而sell、store均为整型,若不进行强转两个int型变量计算结果也是int型,导致计算错误。所以书中在P119页提示使用double定义变量,便于计算,避免程序运行错误。

  • 考虑销售时不允许取出一部分库存,即库存不可拆分时,贪心法如何设计策略?
    若销售时库存不可拆分,则此时贪心算法不一定可以计算出最优解。考虑一种情况: 若销售时库存不可拆分,则此时贪心算法不一定可以计算出最优解。考虑一种情况:
    D = 35
    m1.store = 25,m1.sell =25;m2.store = 20,m2.sell = 20;m3.store =15,m3.sell = 15;
    此时单价相同,选择m1销售则结果错误;可以添加一条规则单价相同时选择库存最少的出售。但是再考虑一种情况:
    D = 40,其他不变。
    上述策略对当前情况不适用。 事实上该问题是0-1背包问题,贪心算法不一定能够给出最优解,可以尝试使用动态规划进行求解。

1.2 区间贪心

问题描述:给定若干开区间,从中选择尽可能多的两两之间没有交集的开区间。例如:(1,3)、(2,4)、(3,5)、(5,7)最多有3个开区间(1,3)、(3,5)、(5,7)没有相交。
输入:若干开区间
输出:两两没有交集的开区间最大数量
代码截图:
在这里插入图片描述
在这里插入图片描述
解题思路:
尽量为剩余的若干区间留下尽可能多的未填充空间。

  • 首先将所有集合的左端点进行非递增排序,找到左端点最大的元素,即为起点并记为lastLow。
  • 遍历排序后的集合,找到第一个不相交的区间(即当前区间右端点<=lastLow),用当前区间左端点值更新lastLow,统计结果+1
  • 输出统计结果

2. 二分法

2.1 二分查找

题目描述:根据给定数据,查找某一元素的位置
输入:待查找的元素
输出:若存在该元素输出其索引值,否则输出-1
代码截图:
在这里插入图片描述
二分查找的时间复杂度是O(logn),是一个高效的查找算法。但是其有一定的限制性:

  • 要求给定集合必须是严格的有序排列。

  • 给定集合存储时必须采用线性表的顺序存储结构实现。

2.2 快速幂

题目描述:计算ab % m
输入:整数a、b、m
输出:计算结果
代码截图:
在这里插入图片描述

扫描二维码关注公众号,回复: 4319252 查看本文章

3. two pointers

3.1 什么是two pointers

题目关键:要求集合是一个有序数列
输入:a + b和的值m
输出:每一个满足 a + b = m的组合
代码截图:
在这里插入图片描述
包括两个队列合并都会采用two pointers编程技巧,在下面的归并排序算法中会有所表现。

3.2 归并排序

归并排序的基本流程如下图所示:
在这里插入图片描述
归并排序递归实现方式代码截图:
在这里插入图片描述
在这里插入图片描述
归并排序算法非递归实现方式代码截图:
在这里插入图片描述
以上归并排序算法实现方式的不足:

  • 分解过程中粒度过细,分解至一个元素才返回进行排序合并 merge方法中,申请空间用于暂时保存元素的排序结果,空间效率较低

  • 针对以上不足可以对算法进行改进,首先在分解过程中,当元素数量小于临界值时不再分解,直接进行排序操作;然后整体采用链式结构实现归并排序算法避免临时空间的申请。

归并排序算法静态链表实现方式代码截图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
输入规模为100000的情况下,原归并排序算法执行一次大约需要40ms,改进后的归并排序算法大约需要20ms。

3.3 快速排序

快速排序算法流程图:
在这里插入图片描述
快速排序算法实现代码截图:
在这里插入图片描述
在这里插入图片描述
思考:当集合本身有序(或大概有序)时,上述快速排序算法的表现如何?
因为每一轮partition方法选择的都是第一个元素作为主元,所以若集合本身有序,所有其他元素都会划分至一个序列中(左侧或右侧),此时算法时间复杂度是O(n2)。可以针对这一不足,对算法主元的选择进行改进(随机选择一个主元)。

改进后的快速排序算法partition方法代码截图:
在这里插入图片描述

4. 其他高效技巧和算法

4.1 打表

打表是一种典型的用空间换时间的技巧(应试技巧),是指通过事先计算将结果并保存得到一个常量表,在之后程序中需要用到时就可以通过查表直接获得结果,进而优化了时间复杂度。
练习:PAT B1044火星数字

4.2 活用递推

PAT B1040 有几个PAT
题目描述:给定只含有P、A、T三个字母的字符串,求共可以形成多少个PAT
输入:长度<105的字符串
输出:PAT的数量 mod 1000000007
代码截图:
在这里插入图片描述

4.3 随机选择算法

题目描述:给定集合,选择第K大的元素
输入:K
输出:第K大的元素值
代码截图:
在这里插入图片描述
随机选择算法的最坏时间复杂度为O(n2),但可以通过证明得到算法的平均时间复杂度是O(n)。

5. 最大公约数和最小公倍数

两个数字的最大公约数可以使用欧几里得算法计算得到,两个数字的最小公倍数等于其乘积除以两个数的最大公约数。
欧几里得算法:gcd(a, b) = gcd(b, a%b),证明略。
代码截图:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43255133/article/details/83619383
今日推荐