跟左神一起做题:无序数组数组,让时间复杂度为0(N),求出排好序之后的相邻最大差值。

本文详细介绍了一种基于桶排序原理的算法,旨在解决无序数组中求取相邻两数最大差值的问题,尤其适用于数据类型为long或longlong的数组,算法时间复杂度为O(N)。文章深入解析了算法步骤,包括获取数组最大最小值、创建并初始化桶数组、遍历元素确定桶归属、计算最大差值等关键环节。
#include<stdio.h> 
int bucket(int num, int length, int max, int min)   //这段代码主要就是把数组里面的每个元素分配到自己的范围的那个桶
{
 	return (int)(num - min)*length / (max - min);  //有误差但是不影响结果
 	//return (num - 9) / 9;
}
int func(int *a, int length)   //主函数主要是遍历数组拿到一个范围,按照这个范围再来进行划分N+1个桶
{
 	int max = a[0];
 	int min = a[0];
 	for (int i = 0; i < length; ++i)   //经这一步遍历整个数组拿到了最大值和最小值
 	{
  		if (a[i] < min)
   		min = a[i];
  		if (a[i]>max)
   		max = a[i];
 	}
 //桶的个数就是数组长度+1
 	int * _1 = new int[length + 1];//第一个数组用来表示的是这个桶里面有没有元素 
 	int * _2 = new int[length + 1];//第二个数组用来记录的是这个桶里面的最大值
 	int * _3 = new int[length + 1];//第三个数组用来记录这个桶里面的最小值
 	for (int i = 0; i <= length; ++i)    //拿到数组之后不要忘记初始化,先把数据初始化成0
 	{
 	 _1[i] = 0;
 	 _2[i] = 0;
 	 _3[i] = 0;
 	}
 	int bid = 0;   //桶的编号
 	int * count = new int[length + 1];     //在这里我又自己添加了两个数组,用这两个数组来记录相对应的最大值和最小值有没有第一个元素进入
 	for (int i = 0; i <= length; ++i)
 	{
 		 count[i] = 0;
 	}
 	int * count2 = new int[length + 1];
 	for (int i = 0; i <= length; ++i)
 	{
 	 	count2[i] = 0;
 	}
 	for (int i = 0; i < length; ++i)           ///这一步下来之后就把每个数字编上了号,编号决定你属于哪个桶
 	{
  		bid = bucket(a[i], length, max, min);
  		if (count[bid] == 0)   //这个是为了判断这个桶这个元素所在编号的桶,里面有没有元素
  		{
  			 _2[bid] = a[i];
   			count[bid]++;
  		}
  		else      //到了这一步就说明这个编号所对应的最大值的数组里面已经有元素来过了,而下面就是比较他俩谁大
  		{
   			if (_2[bid] <a[i])
    			_2[bid] = a[i];
  		}

  		if (count2[bid] == 0)
  		{
  			_3[bid] = a[i];
   			count2[bid]++;
  		}
  		else    ////到了这一步就说明这个编号所对应的最大值的数组里面已经有元素来过了,而下面就是比较他俩谁小
  		{
   			if (_3[bid] > a[i])
    			_3[bid] = a[i];
  		}
  	_1[bid] = 1;
 }
 	//for (int i = 0; i <= length; ++i)
 	//{
  		//printf("%d     %d     %d\n", _1[i], _2[i], _3[i]);
 	//}
 	int res = 0;   /这个是最后用来返回最大值的变量
 	int lastmax = _2[0];   //这个是定义的最大值数组里面的第一个,后面拿的就是
 	for (int i = 1; i <= length; ++i) //用后一个桶的最小值减去前一个桶的最大值
 	{
  		if (_1[i] == 1)    //如果这个范围内存在数字的话,也就是这个桶不为空的情况下
  		{
   			//res = _3[i] - lastmax;
   			if (_3[i] - lastmax > res)        //如果最大值发生改变,那么就交换
   			{
    				res = _3[i] - lastmax;
   			}
   			lastmax = _2[i];  //最后再把前一个桶前移一个
  		}
 	}
 	return res;
}
int main()
{
 	int a[] = { 12, 25, 23, 59, 46, 54, 20, 21, 44, 16 };
 	int c = sizeof(a) / sizeof(int);
 	int p = func(a, c);
 	printf("%d", p);
 	return 0;
}

我就上面的这个排序再重复一遍,以便后面看起来更容易,这个算法使用c语音实现的,意思也就是比较繁琐,比较麻烦。
面试的时候面试官可能会问你,一个无序数组数据类型是long,或者longlong类型的,想让你求出假如这个数组排好序之后相邻两个数的最大值,并且时间复杂度为0(N),首先我们来分析一下,既然时间复杂度是0(N),那么按照我们常规的排序,最少的快排也要o(N*logN),这么说的意思就是首先不能排序,只要拍了序时间复杂度肯定就超了,其次是,无法使用我们之前介绍过的桶排序之计数排序,因为面试官给出的是long甚至longlong类型,也就是说,这组数据里面有正数有负数,也有相差非常的的正数,无法使用计数排序,(计数排序就是一个有范围的无序数组,并且范围不是很大,但是数据量却是很大的那种,那么就可以根据最大数减去最小数的长度建立一个数组,然后遍历这个数组,每次遇到相同的数字就在计数数组的对应位置进行加加操作,完毕之后再根据技术数组,每个元素的个数再将数据重新拷贝回原数组,要是计数数组某个位置为0,则表示该数没有),所以说当面试官给了你元素范围相差巨大的数组后就无法进行桶的计数排序了。
所以我们介绍我上面的这种排序,也很巧妙,根据给你的数组元素个数N,
1.首先遍历这个数组,拿到最大值和最小值
2.然后再根据数组长度N,建立3个长度为N+1的数组(为什么要建立N+1个元素的数组呢?因为我们最终要根据给的元素的最大值和最小值划分桶,每个桶都用自己的范围,也就是说哪个范围的数组进入哪个桶,都是固定的,N个数,N+1个桶,也就是说哪怕每个元素都占一个桶,那么也会存在一个空桶,而空桶所表示的范围和非空的桶是相同的,那么最后在根据空桶的,后一个非空桶的最小值减去前一个非空桶的最大值的时候就不需要考虑桶内部的最大值和最小值了,因为桶内的差值肯定没有隔了一个空桶的差值大),但是又为什么要建立三个数组呢?这三个数组各自代表的含义不相同,第一个数组标识的是对应的这个桶里面是否有数字,有数字的话数组对应的元素就是1,没有的话就是0,然后自比较的时候如果第一个数组对应的桶的数字是0,就不需查看桶里面的最大值和最小值了。第二个数组表示的是对应桶里面的最大值,第三个数组表示的是对应桶里面的最小值。
注意,因为这里我是用的是c写的,建好数组之后要初始化,不然就是一些随机数,所以我们记得把三个数组初始化成0
3.建立三个桶之后,挨个遍历数组中的元素,用这个算法看每个元素属于哪个桶(num - min)*length / (max - min),如果属于哪个桶了,那么哪个桶对应的三个数组对应的桶的内容都要发生改变,如果我们遍历的这个数字是桶里面的第一个元素,那么这个桶的对应第二个数组的最大值就是这个元素本身,如果不是第一个属于这个桶范围的元素,也就是说在他之前已经有一个元素来到过这个桶,已经触发了最大值就是本身了,所以这个时候就要比较第二个进入这个桶的元素的大小和第一次进来的谁大,谁大的话那么第二个数组的对应位置就记录这个最大值,因为第二个数组记录的就是来到过这个桶的最大值。第三个数组也是同样的道理,只不过记录的是最小值。为了记录进入这个桶的数字是这个桶所代表范围内的第一个数字还是不是第一个数字,我又在比较之前建立了两个用来触发的数组,初始化都是0,如果这个范围内的第一个数字来这个桶,就把这个我用来记录的数字变成1,那么第二个数字再来的时候就不会再把自己本身赋给最大还是最小了,而是拿自己和第一次进来的数字进行比较。
4.完了之后就是在这些桶里面相邻的两个非空桶找最大差值,用后一个桶的最小值,减前一个桶内的最大值,然后每次改变前一个桶的编号,和后一个桶的编号来找到,最大的差值,最后再返回。

在这里我还要强调一点关于根据给你的数字个数以及范围,确定桶的个数及每个桶代表的数字范围的问题:
当你遍历完这个数组然后根据(num - min)*length / (max - min),把数字放的桶里面去的时候,最小值一定在第一个桶,最大值一定在最后一个桶,然后中间一定有一个空桶(因为建立的时候就是N+1个),每个桶所表示的范围都是大致相等的不影响最终的结果,因为数字不可能整除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值