求整数最大间隔-性能(hash算法)

题目描述
请输出数字序列的最大间隔。
请使用以下伪随机数生成函数 rand32 () 生成伪随机数
int seed ;
int rand(){ return((( seed = seed * 214013L + 2531011L) >> 16) & 0x7fff); }
int rand32(){undefined
return ((rand() << 16) + (rand() << 1) + rand() % 2);
}
Input Format
2个整数,n seed 其中 2<n<=20000000,seed为随机数种子。

Output Format
整数序列的最大间隔
Example
Input
2000000
1
Output
15737
注意:O(nlogn)以上的时间复杂度至少会有一个案例超时。

样例
输入:
1959000 4910
输出:
16709

 作为刚学数据结构的小白,刚看题时其实还是有点懵的。老师上课讲的散列(哈希)还没有完全搞明白,结果当天晚上12点前就要提交这道题,内心小崩~~~~主要当天课太多,很晚才回宿舍,我自知我能力有限,单靠自己想肯定是完不成了,所以就在CSDN上搜索了一下题目,虽然有的博主发过,但并没有太多讲解就直接上了代码。其实那时候挺崩溃的,虽然很不甘哈,但还是直接Ctrl+C、Ctrl+V了〔╯o╰〕。转天我研究了一下才决定写一写关于这道题的一些想法,既为了自己看着方便也为了以后有的同学理解。

(PS:说明一下,我理解这道题时既依靠了老师的提示,也依靠了网上博主的代码,源代码链接我放在下面。对于源代码我其实有一些意见不同的地方,所以我的代码和原作者的有些出入。也许是我理解不到位哈,如果大家有想法,欢迎在下面留言哦!但也请兄弟姐妹们不要太暴躁哈,毕竟关爱小白人人有责呀(>▽<)!!)

链接:https://blog.csdn.net/weixin_45964488/article/details/111604028

一、明确题目要求

说实话,一开始我并没有理解到底说的是什么意思。有的同学调侃:“题目说了要干什么,但又没完全说。”其实题目的意思是给定一个大小为n的数组(外部输入),将这n个数标在实轴上,问:分为的n-1段有界区间(另加两段无界的,那个就不用算啦)其中,哪一段最长。(如下图)

换句话说就是问将数组的n个元素排完序后,前一个数和后一个数的差(共n-1个)最大是多少。 

二、如何实现

知道了题目的要求,现在我们需要确定做什么。

需要做的就是排序比较

一般来讲我们首先想到的是先通过排序算法将数组排好序,之后再去通过循环去比较每个相邻数的差的大小,是个可行的办法。不过,我们要看一下题目中的一句话:

注意:O(nlogn)以上的时间复杂度至少会有一个案例超时。

现在,反观我们所有的八大排序算法,插排、快排、冒泡排、堆排等等,时间复杂度都不会低于O(nlogn),所以我们需要寻找另一条路径。

这时候hash就登上了“历史舞台”。具体的哈希表(散列表)的概念及相关大家可以去自行搜索,这里就不赘述了。

运用hash算法我们可以将时间复杂度控制在O(n)层级上,可以说是性能极高的算法。不知道大家在看到一个比常规方法性能高得多的算法时是什么想法,我是感到震惊,同时又感到自己就是个大大的 fw (≧ ﹏ ≦)

咳咳,言归正传。具体是算法流程是这样:

                找到数组中的最大、最小值    O(n)  //一趟线性扫描     

将有效范围均匀地划分为n-1段(桶)    O(n)  //相当于散列表   

         通过散列,将各点归入对应的桶    O(n)  //寻找合适的哈希函数     

          在各桶中动态记录最大、最小值   O(n)  //可能相同甚至没有     

       算出相邻(非空)桶之间的“距离”    O(n)  //一趟遍历足矣     

                         最大的距离即maxgap    O(n) 

正确性:maxgap至少与(相邻的)两个桶相交

等价地,定义maxgap的点不可能属于同一个桶

其实整个核心流程就是要找到合适的散列函数,将大小相近的数分到相同的桶中(桶本质上就是数组,用来容纳大小相近的数),经过每个桶内数桶间数的距离比较最后确定出最大的距离,即maxgap

三、代码实现

现在我们就来逐步看一下代码的操作

首先说一点,题目中给出的随机数函数就是用来随机填充数组的,不用多想直接用就可以。

另外,上述功能的实现全部放在了一个函数里

int maxgap(int a[])

 传入的是 main() 函数中用随机数函数生成的随机数数组。

1、找到数组中的最大、最小值

 int amax = a[0], amin = a[0];
    for (int i = 0;i < n;i++)
    {
        if (amax < a[i])amax = a[i];
        if (amin > a[i])amin = a[i];
    }
    if (amax == amin)return 0;

amax表示a数组中的最大值,amin大家也能猜到,用来表示数组中的最小值。

最后的 if 语句大家需要注意一下,如果整个数组的最大最小值是一样的,那么就可以直接下班退出啦,不过可以直接下班的好事还是少呀,唉 ∩﹏∩

有的同学会问这一步有什么用,真的是好问题,所以好问题得在后面说,嘿嘿

2、将有效范围均匀地划分为n-1段(n桶)//相当于散列表

bool* bucket = new bool[n];
    for (int i = 0;i < n;i++)bucket[i] = 0;

bucket[] 只是用来表示该桶是否为空,虽然后面我们要记录每个桶中的最大最小值,但是只要动态记录就可以了,并用不到静态储存,具体的后面会讲,所以这里将其设置成布尔数组就可以啦。

( PS:我参考的那个博主申请的数组大小是 n+1。但我认为即使是最极端的情况下,n个数也只是会被分入 n 个桶,所以申请 n 就可以了,并用不到 n+1。)

3、 通过散列,将各点归入对应的桶 ;在各桶中动态记录最大、最小值(原3、4步合并)

int* imax = new int[n];
    int* imin = new int[n];
    double gap = double(amax - amin) / (n - 1);
    for (int i = 0;i < n;i++)
    {
        int index = int((a[i] - amin) / gap);
        if (bucket[index] == 0)
        {
            imax[index] = a[i];
            imin[index] = a[i];
            bucket[index] = 1;
        }
        else
        {
            imax[index] = max(a[i], imax[index]);
            imin[index] = min(a[i], imin[index]);
        }
    }

imax[] 用于记录每个桶中的最大值,imin[] 则用于记录每个桶中的最小值 ,index表示将每个数归入的桶的下标。

我觉得这一段代码的关键是 gap 的计算与 index 的确定。当时我的疑问就是为什么要这么算。现在我来尝试解释一下。

首先,我们要将数组中的每个数归入桶中,而桶其实就是前面的 bucket[],我们在申请数组时的下标只能是连续的从 0 到 n-1 ,而不可能是跳跃的下标。所以要想办法通过操作将每个数通过计算分入不同的桶,而且算出的下标(index)不能大于n-1。gap的计算方法大家可以看到,它实际上就是一个标尺,通过计算每个数与最小值的差与gap的倍数来确定进入哪个桶。最小的情况是 a[i] 等于amin,结果 index 为 0;最大情况是 a[i] 等于amax,结果index为 n-1,正好符合需求。【其实我认为这种处理方式大家可以记住,下此遇到类似的排序情况就可以直接使用了】

后面的代码大家基本上就可以看明白。就是动态存储了每个桶中的最大最小值,以及将非空的桶置为1。以上的过程就相当于进行了数据排序,只不过时间性能有了提升!强调一点就是注意gap是double 类型,因为是标尺还是越精确越好,而后面的index需要是整型,因为下标只能是整数。

( PS:原博主申请的记录各桶最大最小值的数组大小都是n+1,但我觉的n就足够了,理由同上)

 4、算出相邻(非空)桶之间的“距离” 

int lastmax;
int maxgap = 0;
    for (int i = 0;i < n;i++)
        if (bucket[i] != 0)
        {
            lastmax = imax[i];
        }
    for (int i = 1;i < n;i++)
    {
        if (bucket[i] != 0)
        {
            int tem = max(imin[i] - lastmax, imax[i] - imin[i]);
            maxgap = max(maxgap, tem);
            lastmax = imax[i];
        }
    }

 lastmax用于表示前一个桶的最大值。

声明maxgap用于存储最终结果,虽然代码里写的是初值为1,但我认为更应该设置为INT_MIN(即最小的整型数字),因为我们也不确定是否有负数。但是设为0可以通过并且学校OJ并不识别INT_MIN,所以我这里就继续使用0了。

第一个for循环用于找到第一个非空桶,并将其中的最大值赋给lastmax。

第二个for循环就是要比较找出最大的差值,比较的三者是现在所处桶的最小值与前一桶的最大值的差现在所处桶的最大值与最小值的差、以及maxgap本身

(PS:原博主在第二个循环中的比较是下面那样的。但我认为所处桶内的最大最小的差不能被忽视,即使它们之间的距离可能较短,但还是必须要考虑的一点)

for (int i = 1; i <= n; i++) 
	{
		if (hasNum[i])  
		{
			maxGap = max(maxGap, (mins[i] - lastMax));
			//cout << mins[i] << ' ' << lastMax << endl;
			lastMax = maxs[i];
		}
	}

好啦,以上就是这道题的全部内容啦。这是我第一次写文章,总归有不足,不管是排版还是思路,亦或者能力上的缺陷也请各位同学们指出哈。凡事都是有第一次,第一次写不好,第二次一定会有进步。

蟹蟹大家啦!!!!!!!!

哦,差点忘了完整代码~~~

/*
请输出数字序列的最大间隔。
请使用以下伪随机数生成函数 rand32 () 生成伪随机数
int  seed  ;
int rand(){ return((( seed   =  seed   * 214013L + 2531011L) >> 16) & 0x7fff); }
int rand32(){
return ((rand() << 16) + (rand() << 1) + rand() % 2);
}
*/
#include<iostream>
using namespace std;
int n,seed;
int rand()
{
    return(((seed = seed * 214013L + 2531011L) >> 16) & 0x7fff);
}
int rand32()
{
    return ((rand() << 16) + (rand() << 1) + rand() % 2);
}//随机数
int maxgap(int a[])
{
    int maxgap = 0;
    int amax = a[0], amin = a[0];
    for (int i = 0;i < n;i++)
    {
        if (amax < a[i])amax = a[i];
        if (amin > a[i])amin = a[i];
    }//找到数组中的最大、最小值
    if (amax == amin)return 0;
    bool* bucket = new bool[n];
    for (int i = 0;i < n;i++)bucket[i] = 0;//将有效范围均匀地划分为n-1段(n桶)//相当于散列表
    int* imax = new int[n];
    int* imin = new int[n];
    double gap = double(amax - amin) / (n - 1);
    for (int i = 0;i < n;i++)
    {
        int index = ((a[i] - amin) / gap);
        if (bucket[index] == 0)
        {
            imax[index] = a[i];
            imin[index] = a[i];
            bucket[index] = 1;
        }
        else
        {
            imax[index] = max(a[i], imax[index]);
            imin[index] = min(a[i], imin[index]);
        }
    }//通过散列,将各点归入对应的桶 ;在各桶中动态记录最大、最小值
    int lastmax;
    for (int i = 0;i < n;i++)
        if (bucket[i] != 0)
        {
            lastmax = imax[i];
        }
    for (int i = 1;i < n;i++)
    {
        if (bucket[i] != 0)
        {
            int tem = max(imin[i] - lastmax, imax[i] - imin[i]);
            maxgap = max(maxgap, tem);
            lastmax = imax[i];
        }
    }//算出相邻(非空)桶之间的“距离” 
    return maxgap;
}
int main()
{
    cin >> n >> seed;
    int* np = new int[n];
    for (int i = 0;i < n;i++)
        np[i] = rand32();
    cout << maxgap(np);
    system("pause");
    return 0;
    
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值