继续贪心:删数问题

上次已经讲过一次贪心了,这次就来个小实战吧,下面看题:
**

删数问题

**

已知一个数组,要求从数组中删除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语言基础课,改天我会总结一下比较有意思的知识点,跟大家分享一下。
我是算法吹,以后会给大家带来更多精彩的算法。这里写图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值