算法-再探无序数组中位数问题
算法-再探无序数组中位数问题
给定一个无序数组,求无序数组的中位数
本问题在前面写的博客里面提到过算法-TopK相关的问题 。可以用堆实现或者用快速选择算法实现。在使用快速选择算法实现时,分为两种情况讨论,一种是数组元素数量为奇数时,我们直接选择中间元素即可,另一种是数量为偶数时,我们做两次快速选择。
但上述方法中,在数组元素数量为偶数时,并没有关注到第一次快速选择对数组的影响,导致第二次快速选择的时间复杂度可能达到O(N^2)
事实上,在我们可以利用快速选择的性质,将第二次选择时间复杂度降低到O(k-1)
快速选择的原理就是将枢纽元调整到位置k-1处,最终,枢纽元左边的元素大于等于枢纽元且右边元素小于等于枢纽元,或者反之。
在我们选出第k大的元素时,实际上,我们就能确定第k-1大的元素在枢纽元的左半部分或者是右半部分。
以选择第K大的元素为例,我们将数组按降序选择。
1、在数组长度为偶数时,我们需要选择第nums.length/2+1大的元素(记为m1),以及nums.length/2大的元素(记为m2)。在我们选择出m1,我们就可以确定,m2在k-1的左半部分,又因为左半部分全都大于m1,因此,问题变成求[0,k-1)区间的最小值,也就是m2,我们直接遍历区间的元素找最小值m2即可。最终的结果就是(m1+m2)/2。
2、在数组长度为奇数时,我们直接选择第nums.length/2+1大的元素(记为m1),m1就是我们要找的中位数
如此,我们就可以得到一份更快更简单的代码:
@Test
public void test(){
int[] nums={
7,5,9,3,11,32,27,1,-9,4};
//-9,1,3,4,5,7,9,11,27,32
System.out.println(mediumNUmberOfMixArray(nums));
}
public int mediumNUmberOfMixArray(int[] nums){
int k=nums.length/2+1;
quickSelect(nums,0,nums.length-1,k);
int m1=nums[k-1];
if(nums.length%2==1){
return m1;
}else {
int m2=Integer.MAX_VALUE;
for (int i=0;i<k-1;i++){
m2=Math.min(m2,nums[i]);
}
return (m1+m2)/2;
}
}
public void quickSelect(int[] nums,int left,int right,int k){
if(left<right){
int i=left,j=right;
int pivot=nums[left];
while (i<j){
while (i<j&&nums[j]<=pivot){
j--;
}
while (i<j&&nums[i]>=pivot){
i++;
}
if(i<j){
swap(nums,i,j);
}
}
swap(nums,i,left);//恢复枢纽元
if(i>=k){
quickSelect(nums,left,i-1,k);
}else {
quickSelect(nums,i+1,right,k);
}
}
}
private void swap(int[] nums, int i, int j) {
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}