首答,欢迎追问与提意见,先说我的思路,用桶,以空间换时间的方式得到最快速度的答案,C代码在最下面,已测试。
测试结果:
思考了一会,按照常规方法走,如果没有特殊要求,用排序是可以的。最快的排序算法是时间复杂度是 O(nlogn)
十万的数据量的复杂度,也就是进行 100000*log(100000) = 17万次运算。
以现在计算机的运算速度(单位是每秒百万条指令),如果你不是做一些有时间限制的竞赛(ACM)的话,是完全可以的。
但这道题如果放在ACM竞赛中,如果用排序方法去做应该是铁铁的超时了。
于是我用桶(C语言桶的概念不懂请百度)做了一个以空间换时间的折中算法,时间复杂度是O(n),也就是10万次运算。空间复杂度取决于最大数字。如果最大数字是10万,就要开辟10万的数组空间,如果最大数字是20万,就要开辟20万的数组空间。如果最大数字是一百万,就要开辟一百万数组大小的空间,C语言不支持开辟这么多连续的内存空间,如果你真有这个问题,可以在segmentfault上提一个新问题,或者在答案下追问。
具体思路:
结果分为两个堆:0堆和1堆。
用桶存放所有元素的出现次数,对桶进行遍历,记录数组中所有的元素出现次数之和为count,当count>=N/2时(N在这里为10万),停止遍历。
[1] 这时遍历过的就是在左边堆的了。没遍历过的就是在右边堆里的了。
比如
3 2 1 4 5 3
排序后 1 2 3 3 4 5
桶(记为b)的状态: b[0]=0, b[1]=1, b[2]=1, b[3]=2, b[4]=1, b[5]=1
当遍历到b[3]的时候,count为4,已经大于3了(元素个数/2)。这时候记录一下中间数的位置
然后根据上面思路[1] 就很容易得出1 2 3 3 都是在左边堆的,
4 5 都是在右边堆的。
这时候出现了一个bug,有两个3,其中1个3应该在右边。
这个问题很好解决,上例中,count在停止记数的时候为4,4与3相比较多了1,证明多加了一个。所以要把最后一个移到右边堆。
C语言代码
把N改成10万,记得把printf该关的关掉。
#include
#include
#define N 19
#define MAX 32767
int main()
{
int a[N] = {0}, b[MAX] = {0};
int count = 0, position = -1;
printf("the random data is \n\n");
for(int i=0; i
{
a[i] = (int)(rand() % MAX); //产生MAX以下的随机数
b[a[i]]++; //统计每个数字出现的次数
printf("%5d ",a[i]);
}
for(int i=0; i
{
if(b[i] == 0)
continue;
count += b[i]; //统计从小到大所有的数字出现的次数
if(count >= N/2)
{
position = i; //找到分界点时记录位置
break;
}
}
printf("\n-----------------\n\nthe result is\n\n");
int temp = 0;
for(int i=0; i
{
if(a[i] < position)
printf(" left ");
else if(a[i] == position)
{
if(++temp <= b[a[i]]-(count-N/2)) //左右分界点的值出现多次的时候,控制一下。
printf(" left ");
else
printf("right ");
}
else
printf("right ");
}
printf("\n\n>>>>>> Middle is %5d\n",position);
return 0;
}
PS:其实我感觉应该有不浪费空间又可以节省时间的方法,但想了半天没想出来,如果有更好的答案望分享。
我是学Java的,也专门学习过一些算法知识,正在往全栈方面发展。有兴趣可以加个兴趣群一起交流,群号374660776