两种数字删除问题算法比较
数字删除问题是典型的贪心算法问题,也是最简单的贪心算法,通过学习数字删除问题可以了解并理解贪心算法,可在以后的算法设计中灵活运用思路。
本文对数字删除问题给出了两种解法用来做比较,一种用覆盖删除的算法,一种贪心算法。所谓贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。它所做的每一个选择都是当前状态下具有某种意义的最好选择,即贪心选择;并且每次贪心选择都能将问题化简为一个更小的与原问题具有相同形式的子问题。文中首先描述问题,然后对其分析,证明其具有最优子结构性质和贪心选择性质,紧接着对分析次算法的复杂度。
1、数字删除问题的描述
给定n位正整数m,去掉其中任意1≤k<n个数字后,剩下的数字按原次序排列组成一个新的正整数。且这个正整数是去掉k位数字之后最小的。
对于给定的n 位正整数m和正整数k,设计一个算法找出剩下数字组成的新数最小的删数方案。
例如正整数m为2658941,去掉其中n为5个数字后,剩下的数字按原次序组成新数最小的为数值21。
当去掉的数字位数大于正整数位数时,将会给出超位提醒。
2、算法分析
2.1 不具备贪心算法的覆盖删除算法分析
对于n位正整数m可以表示为, 去掉其中任意k位数,其中1≤k<n,则余下的数字按原次序排列组成一个新的正整数,且这个正整数最小。假设数字在去掉删除数后的最优解为
,即此数为按原次序排列组成一个新的最小正整数。
算法中采用了一种覆盖删除的方法,即如果,则不用采取覆盖,保持原次序,若
,即后一位比前一位大时,则
,利用覆盖删除的方法将前一位大数删除,并由后一位的小数代替。将此逻辑依次从
比较到
,则得出的正整数位数仍为n,即为最小正整数
。
对于而言,删除一位数字后,原问题
变成了对n位数字删除k-1个数的新问题。新问题和原问题相同,问题规模n位正整数没有变化,而删除都掉的个数由k减少为k-1。对于剩下的数字,将继续利用覆盖删除的方法进行下去。而且每一次覆盖删除过后便会从头开始,重新进行比较,然后覆盖。若n-k=2,则基本这时m已经变成了
(以通过比较发现
最小时)仍然为n位,通过输出指针只读取两次,则输出的为
是最小正整数。
2.2 贪心算法算法分析
对于n位正整数m可以表示为, 去掉其中任意k位数,其中1≤k<n,则余下的数字按原次序排列组成一个新的正整数,且这个正整数最小。假设数字在去掉删除数后的最优解为
,即此数为按原次序排列组成一个新的最小正整数。
算法中采用贪心算法方法,即如果,则不采取措施,保持原次序,若
,即后一位比前一位大时,则删除
(对于其他的数字也是相同的,此处只是举了一个具体的例子,便于大家理解,其具有代表性)。得到的数字就是n-1位中最小的正整数,即为
。
对于而言,删除一位数字后,原问题
变成了从
对n-1位数字删除k-1个数的新问题。新问题和原问题相同,问题规模n减小为n-1,删除的数字个数由k减少为k-1。对于剩下的数字,将继续利用此方法进行下去,直至删去k个数为止。删除k个数字过后剩下的便是n-k位中最小的数值了。
2.3贪心选择性质证明
根据m数的位数及数值的特性,对其分解则:m=。
进行第一次数字删除时:m(1)=
假设删除的不是时:m(2)=
因为,且,所以m(1)<m(2)
因此数字删除问题满足贪心选择性质。
3、程序实现与分析
3.1基于C语言的覆盖删除数字程序实现:
1 fp=fopen("input.txt","r");//读取文档内容 2 if(fp==NULL){ 3 printf("打不开在此文件!\n"); 4 exit(0); 5 } 6 fscanf(fp,"%s%d\n",a,&n); 7 if(n>=strlen(a)) //如果要删除的位数大于等于输入的数字位数,将给出提示! 8 { 9 printf("删除位数过大!\n"); 10 return; 11 } 12 while(n>0) 13 { 14 i=0; //每次开始将i初始化为0,表示重新开始检测下降点 15 while(i<strlen(a) && a[i]<=a[i+1]) //算法最为关键的部分 ,检测从第几位开始进行删除覆盖 16 i++; 17 for(j=i;j<strlen(a);j++) //覆盖实现删除效果 18 a[j]=a[j+1]; 19 n--; 20 } 21
input.txt:
2658941
5
output.txt:
21
从第一位数开始一直比较到最后一位,若前一位比后一位大则后一位将前一位覆盖。
3.2满足贪心选择性质的贪心算法程序实现:
1 while(k>x&&m==0) 2 {i=i+1; 3 if(a[i-1]>a[i]) //出现递减,删除递减的首数字 4 {printf("%d",a[i-1]); 5 for(j=i-1;j<=n-x-2;j++) 6 a[j]=a[j+1]; 7 x=x+1; //x统计删除的个数 8 i=0; //从头开始查递减区间 9 } 10 if(i==n-x-1) //已无递减区间,m=1脱离循环 11 m=1; 12 } 13 printf("\n删除后所得最小数:");
若出现递减,则直接删除递减的首数字。
4、两种算法对比
覆盖删除算法虽然程序设计与实现比较简单但是对于计算机来说计算量比较大,而且此程序运行时会一一做比较,比较完了之后若有满足条件的便会覆盖掉,当覆盖过后程序不会接着往下运行,它会重新开始再次进行比较,看是否满足条件的,计算量比较大,若数字比较庞大的话,会比较浪费时间,算法时间复杂度为O(n2),空间复杂度为O(n2)。而对于贪心算法而言,就相对简单了,数字删除问题贪心算法思路是每一步总是选择一个使剩下的数最小的数字删除,即按高位到低位的顺序搜索,若各位数字递增,则删除最后一个数字;否则删除第一个递减区间的首字符,这样删一位便形成了一个新的数字串。按上述规则再删除下一个数字,与覆盖删除不同的是,它不会回头,会一直比较下去,具有贪心选择性质和最有子结构性质,而且它的算法时间复杂度为O(n),空间复杂度为O(n),比覆盖删除小很多,在算法运行上面也会节约很多时间。