您的位置:首页 > 职场人生

乱序不重复整数数组求中位数

2015-11-03 17:29 459 查看
今天去面试遇到一道算法题,40亿整数乱序不重复的情况下,如何取它的中位数,要求时间复杂度为O(n)。

首先想到的是快排加二分查找的思想,取一个数进行partition之后确定它的绝对位置,就可以放弃少的一边的比较。

搜了一下发现有个QuickSelect算法,也一起放进来对比了。

最后仔细看了一下,QuickSelect算法其实就是快排加二分。。。一样的思路。

最后还有一种是用数组的,不过如果能用bitmap可以省下很多内存。同学说时间复杂度也是O(n),但是可能因为语言的关系,往数组存1花了很多时间。代码贴一下吧:

package cn.springmvc.test;

import java.util.Arrays;
import java.util.HashSet;

public class UserTest {

/**
* @param args
*/
public static void main(String args[]) {
HashSet<Integer> hashSet = new HashSet<Integer>();
int maxSize = 4000000;
int random  = 10000000;
while(hashSet.size()<maxSize){
int x=(int)(Math.random() * random);
hashSet.add(x);
}
int[] arr = new int[maxSize];
int[] arr2 = new int[maxSize];
int[] arr3 = new int[maxSize];
int[] arr4 = new int[maxSize];
int max = 0;
Integer[] a =hashSet.toArray(new Integer[0]);

int aLength = a.length;
for(int i=0;i<aLength;i++){
arr[i]=a[i];
arr2[i]=a[i];
arr3[i]=a[i];
arr4[i]=a[i];
}
//数组算法
long before0 = System.currentTimeMillis();
for(int i=0;i<aLength;i++){
if(max<a[i])max = a[i];
}
int[] arr5 = new int[max+1];
for(int i=0;i<aLength;i++){
arr5[a[i]]=1;
}
int counter = 0;
int ii=0;
for(int i=0;i<arr5.length;i++){
counter = counter + arr5[i];
ii=i;
if(counter==aLength/2)break;
}
System.out.println(System.currentTimeMillis()-before0);
System.out.println(ii);

//QuickSelect
long before = System.currentTimeMillis();
int rs = quick_select(arr,maxSize);
System.out.println(System.currentTimeMillis()-before);
System.out.println(rs);

//快排加二分
long before3 = System.currentTimeMillis();
quick_sort1(arr3,0,maxSize-1,(maxSize-1)/2);
System.out.println(System.currentTimeMillis()-before3);
System.out.println(arr3[(maxSize-1)/2]);

//Arrays快排
long before2 = System.currentTimeMillis();
Arrays.sort(arr2);
System.out.println(System.currentTimeMillis()-before2);
System.out.println(arr2[maxSize/2-1]);

//普通快排
long before4 = System.currentTimeMillis();
quick_sort(arr4,0,maxSize-1);
System.out.println(System.currentTimeMillis()-before4);
System.out.println(arr4[(maxSize-1)/2]);
}

public static int quick_select(int[] arr,int n){
int low, high;
int median;
int middle, ll, hh;

low = 0 ; high = n-1 ; median = (low + high) / 2;
for (;;) {
if (high <= low) /* One element only */
return arr[median];

if (high == low + 1) {  /* Two elements only */
if (arr[low] > arr[high])
ELEM_SWAP(arr, low,high) ;
return arr[median] ;
}

/* Find median of low, middle and high items; swap into position low */
middle = (low + high) / 2;
if (arr[middle] > arr[high])    ELEM_SWAP(arr, middle,high) ;
if (arr[low] > arr[high])       ELEM_SWAP(arr, low,high) ;
if (arr[middle] > arr[low])     ELEM_SWAP(arr,middle, low) ;

/* Swap low item (now in position middle) into position (low+1) */
ELEM_SWAP(arr,middle, low+1) ;

/* Nibble from each end towards middle, swapping items when stuck */
ll = low + 1;
hh = high;
for (;;) {
do ll++; while (arr[low] > arr[ll]) ;
do hh--; while (arr[hh]  > arr[low]) ;

if (hh < ll)
break;

ELEM_SWAP(arr,ll, hh) ;
}

/* Swap middle item (in position low) back into correct position */
ELEM_SWAP(arr,low, hh) ;

/* Re-set active partition */
if (hh <= median)
low = ll;
if (hh >= median)
high = hh - 1;
}
}
public static void ELEM_SWAP(int[] arr,int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}

static int AdjustArray(int s[], int l, int r) //返回调整后基准数的位置
{
int i = l, j = r;
int x = s[l]; //s[l]即s[i]就是第一个坑
while (i < j)
{
// 从右向左找小于x的数来填s[i]
while(i < j &&
9910
s[j] >= x)
j--;
if(i < j)
{
s[i] = s[j]; //将s[j]填到s[i]中,s[j]就形成了一个新的坑
i++;
}

// 从左向右找大于或等于x的数来填s[j]
while(i < j && s[i] < x)
i++;
if(i < j)
{
s[j] = s[i]; //将s[i]填到s[j]中,s[i]就形成了一个新的坑
j--;
}
}
//退出时,i等于j。将x填到这个坑中。
s[i] = x;

return i;
}

static void quick_sort1(int s[], int l, int r,int middle)
{
if (l < r)
{
int i = AdjustArray(s, l, r);//先成挖坑填数法调整s[]
if(i>middle){
quick_sort1(s, l, i - 1,middle); // 递归调用
}else if(i<middle){
quick_sort1(s, i + 1, r,middle);
}else return;
}
}

static void quick_sort(int s[], int l, int r)
{
if (l < r)
{
//Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1
int i = l, j = r, x = s[l];
while (i < j)
{
while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
j--;
if(i < j)
s[i++] = s[j];

while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
i++;
if(i < j)
s[j--] = s[i];
}
s[i] = x;
quick_sort(s, l, i - 1); // 递归调用
quick_sort(s, i + 1, r);
}
}
}


结果是快排加二分的速度跟QuickSelect的速度基本是一样的。不知道有没有更快的方法?

参考

- http://codepad.org/pQsNnpqd

- http://blog.csdn.net/morewindows/article/details/6684558
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 面试