继续贪心:删数问题
2016-05-17 23:27
218 查看
上次已经讲过一次贪心了,这次就来个小实战吧,下面看题:
**
首先,我们学习贪心算法,其实并没有什么用,因为大部分人即使不学贪心算法也会有贪心的思想,而且算法本身只是思想,空有一身思想是没有任何用的,写出什么程序,完全是看自己如何思考的。
所以,我们的目的不只是如何贪心,而是如何尽可能的贪。
首先,从逻辑上讲,删数和选数是一样的。删n个数等价于选len-n个数。那么我们可以将删数转化成选数问题。那么我们根据什么标准来选数呢?是每次选最小的吗?就上面的例子说,如果要删除五个数,那就是选择两个数,结果很明显是 0 5,但是假如每次选最小的数,得到的结果是 1 0,是不正确的。所以可以排除每次选最小的方案,但是选择小的肯定不会错,分析发现,当第一次选择最小的数,这个数把数组分成两部分,下次选择时,只要这个最小的数后面还有数,那么一定优先选后面的数,当后面的数选完时,才选择前面的数。
那么看看逻辑吧:
数组中有len个数,需要选择n个数,我们先选择一个最小的,先假设这个最小的数的下标为i,那么还需要选择n-1个数,这n-1个数优先从最小的数的后面选,最小的数后面一共还有len-i个数,那么就有两种情况:
①:len-i > n-1 ;从最小的数后面的数里选择n-1个数;
②:len-i < n-1 ;选择最小的数后面的全部数,然后从最小的数前面选择n-1-len+i个数;
当然,这样做有个问题:因为是每次选择一个数,我们该如何记录选择的数的原顺序?
其实这个问题可以很好地用树的思想解决:
我们可以利用遍历树的方式,先计算最小的数之前的部分,然后输出最小的数,在计算最小的数之后的部分,使用递归实现,很简单的。
函数的简单结构如下:
delete()
{
delete(font); 计算最小的数之前的部分
put(min); 输出最小的数
delete(last); 计算最小的数之后的部分
}
//上面只是简单的逻辑,用C语言实现的话还得费些功夫,没什么难度,但是比较麻烦,最麻烦的是数组下标的控制,大家可以结合我的代码分析分析。
特殊情况:当你只需要删除0个数时,将原数组整个输出一遍就行了,不必继续往下递归了,这样可以节约很多时间。
下面放出我的代码:
贪心很简单,不如动规那样繁琐,所以到这里贪心算法基本上可以结了,希望大家多敲代码,多磨练自己。最近在给一个大一的学弟上C语言基础课,改天我会总结一下比较有意思的知识点,跟大家分享一下。
我是算法吹,以后会给大家带来更多精彩的算法。
**
删数问题
**已知一个数组,要求从数组中删除n个数,要求剩下的数按顺序排列成的数字最小。例:数组为{1,3,2,4,7,0,5} n = 3; 输出结果为: 1 2 0 5
拿到题先分析,首先分析上面的例子试试吧,既然要删除n个数,那就一个一个删吧,那么删的时候要遵循什么标准呢?只要每次删的时候依次枚举删后的结果,挑选一个最小的就好了。因为每次删数都需要遍历当前数组里的有效值,需要删n个数,所以时间复杂度大概为O(n*len);但是,我们每次删除一个数时,都要将数组里的有效元素一一提取出来,合并成一个整数,才可以进行比较,这个合并的过程时间复杂度为O(len);当然,你可以直接按顺序从数组头部到尾部依次比较,这样可以优化一些,但是无法改变本质,最坏时间复杂度依然是O(len);这种方法也很简单,所以我就不讲这个算法了,下面提供另一种思路。首先,我们学习贪心算法,其实并没有什么用,因为大部分人即使不学贪心算法也会有贪心的思想,而且算法本身只是思想,空有一身思想是没有任何用的,写出什么程序,完全是看自己如何思考的。
所以,我们的目的不只是如何贪心,而是如何尽可能的贪。
首先,从逻辑上讲,删数和选数是一样的。删n个数等价于选len-n个数。那么我们可以将删数转化成选数问题。那么我们根据什么标准来选数呢?是每次选最小的吗?就上面的例子说,如果要删除五个数,那就是选择两个数,结果很明显是 0 5,但是假如每次选最小的数,得到的结果是 1 0,是不正确的。所以可以排除每次选最小的方案,但是选择小的肯定不会错,分析发现,当第一次选择最小的数,这个数把数组分成两部分,下次选择时,只要这个最小的数后面还有数,那么一定优先选后面的数,当后面的数选完时,才选择前面的数。
那么看看逻辑吧:
数组中有len个数,需要选择n个数,我们先选择一个最小的,先假设这个最小的数的下标为i,那么还需要选择n-1个数,这n-1个数优先从最小的数的后面选,最小的数后面一共还有len-i个数,那么就有两种情况:
①:len-i > n-1 ;从最小的数后面的数里选择n-1个数;
②:len-i < n-1 ;选择最小的数后面的全部数,然后从最小的数前面选择n-1-len+i个数;
当然,这样做有个问题:因为是每次选择一个数,我们该如何记录选择的数的原顺序?
其实这个问题可以很好地用树的思想解决:
我们可以利用遍历树的方式,先计算最小的数之前的部分,然后输出最小的数,在计算最小的数之后的部分,使用递归实现,很简单的。
函数的简单结构如下:
delete()
{
delete(font); 计算最小的数之前的部分
put(min); 输出最小的数
delete(last); 计算最小的数之后的部分
}
//上面只是简单的逻辑,用C语言实现的话还得费些功夫,没什么难度,但是比较麻烦,最麻烦的是数组下标的控制,大家可以结合我的代码分析分析。
特殊情况:当你只需要删除0个数时,将原数组整个输出一遍就行了,不必继续往下递归了,这样可以节约很多时间。
下面放出我的代码:
#include <stdio.h> #include <stdlib.h> static int delete(int *, int, int); static int low(int, int); int main() { int a[] = {1,3,2,4,7,0,5}; // int a[] = {1,2,6,3,4}; int n = sizeof(a) / sizeof(a[0]); delete(a,n,n-4); return 0; } int delete(a,n,d) int * a; int n, d; { if (d <= 0) return 0; if (n == d) { for (int i = 0; i < n; i++) printf("%4d",a[i]); return n; } int min = 0; for (int i = 1; i < n; i++) { if (a[i] < a[min]) min = i; } delete(a,min,d-n+min); printf("%4d",a[min]); delete(&a[min+1],n-min-1,low(n-min,d)-1); return n; } int low(int x, int y) { return x<y?x:y; }
贪心很简单,不如动规那样繁琐,所以到这里贪心算法基本上可以结了,希望大家多敲代码,多磨练自己。最近在给一个大一的学弟上C语言基础课,改天我会总结一下比较有意思的知识点,跟大家分享一下。
我是算法吹,以后会给大家带来更多精彩的算法。
相关文章推荐
- 很好的Linux学习书,推荐大家看看
- RCNN,Fast-RCNN,Faster-RCNN
- jarsigner: 无法打开 jar Eclipse打包时出现export aborted because fatal lint errors were found android反编译重新签名问题
- 第十二周上机实践——项目1-实现复数类中的运算符重载-(2)
- LR分析器,自下向上分析法
- 用户体验要素是什么,产品设计一般步骤是什么
- Java安装
- Java代码可视化之路#1
- 【转载】D3DXMatrixLookAtLH视图变换函数详解
- sql server —表的管理
- HDU 2602 Bone Collector (01背包)
- Python performance
- LCA(倍增在线算法) codevs 2370 小机房的树
- PySide——Python图形化界面
- Improving the quality of the output
- ●获取汉字全拼
- 波特率跟比特率或者传码率跟传信率的区别
- Python 代码性能优化
- Linux Eclipse C/C++ 报错 launch failed
- 安卓WebView的那些坑