1.求第k小的数
查找无序数组中第K小的数,利用快速排序的思想,这里使用partition的改装版,使用随机数选取主元,避免了最坏的情况,先选取一个支点,将所有元素分成两部分,看看这个支点是第几小的数,然后再判断。时间复杂度为 n
计算出主元为第i小的数
(1)主元为第i=k小的数,则返回找到的查询结果。
(2)主元是第i<k,则继续在左部分寻找第k小的数
(3) 若住元是第i>k,则在右边部分寻找第k-i小的数。
代码如下
/**
*
* 随机数方法求第k小的数
*/
public int randomPartition(int a[],int low,int high)
{
int len=high-low+1;
Random rand=new Random();
int r=rand.nextInt(len)+low;
int temp=a[r];
a[r]=a[high];
a[high]=temp;
int i=low-1,j=low;
int key=a[high];
for(;j<high;j++)
{
if(a[j]<=key)
{
i++;
int temp1=a[j];
a[j]=a[i];
a[i]=temp1;
}
}
i++;
a[j]=a[i];
a[i]=key;
return i;
}
public int randomSelect(int a[],int low ,int high,int i)
{
if(low==high)
return a[low] ;
int q=randomPartition(a, low, high);
int k=q-low+1;
if(i==k)
return a[q];
else if(i<k)
return randomSelect(a, low, q-1, i);
else
return randomSelect(a, q+1, high, i-k);
}
很简单的问题,由于粗心 一个交换值的语句,变量名称写错了,调试了 好长时间,一定不要出这种错。
2.求k分位数
这里求k分位数转化为求第k小的数 ,先求n/k小的数,再在n/k+1到结束求n1/(k-1)小的数,如此递归。
代码:
/**
* @param a
* @param low
* @param high
* @param k
* 求k分位数
*/
public void kQuintile(int a[],int low,int high,int k)
{
if(k>1)
{
int len=high-low+1;
int i=len/k;
int d=randomSelect(a, low, high, 2);
System.out.println(d+" ");
kQuintile(a, i+low, high, --k);
}
}
3.互异元素集合S,对于K<=n,求S中最接近中位数的K个元素。
1.找S的中位数x,将S中的每个数与x相减,再求绝对值。//注意每个数的下标与其绝 对值建立关系。
2.在n隔绝对值中找第k小y;
3.一次检查每个绝对值,如果他小于等于y,那么对应的数是与x最近的k个数之一。
T(n)=O(n);
4.查找两个有序数组的中位数,长度都为n
查找两个有序数组中的中位数,并且时间复杂度为lgn(要想到二分查找的时间复杂度也是这样,所以要利用这种思想),不能先对这两个数组进行排序,因为肯定时间复杂度会超标。所以依旧利用分治的思想,想一下中位数可能存在的情况,在A数组,或者B数组,不论在那一个数组利用中位数前 面有n-1个元素判断是否为中为数,或者另一方向思考,假设是中位数,则与B数组中的元素满足什么关系,注意当A[len-1]为中为数,此时只要判断A[len-1]<=B[0]就行了!若果不加这一条会有数组越界。一次查找A中哪一个元素符合条件,因为有序所以可以使用二分查找。
我们先从数组A开始找。考察数组A中的一个元素A[p], 在数组A中,
有 p 个数比A[p]小,如果数组B中恰好有 c-p 个数比 A[p] 小, 则俩数组合并后就恰好有 c 个数比A[p]小,
于是A[p]就是要找的中位数。 如下图所示:
如果A[p] 恰好位于 B[c-p-1] 和 B[c-p] 之间,则 A[p] 是中位数
如果A[p] 小于 B[c-p-1] ,说明A[p] 太小了,接下来从 A[p+1] ~A[m-1]开始找
如果A[p] 大于 B[c-p] ,说明A[p] 太大了,接下来从 A[0] ~A[p-1]开始找。
如果数组A没找到,就从数组B找。
代码:
/**
* @param x
* @param y
* 查找两个有序数组的中位数,这里取得下中位数,中位数为两个数组排序后的第n个元素,
* 不利用合并排序,因此使用性质 中位数前面有几个数,可能在a中也可能在b中,在每个数组中查查时使用
* 二分查找
*/
public int twoArrayFindMedian(int []x,int []y)
{
int low=0;
int high=x.length-1;
if(findMdian(x, y, low, high)!=-1)
return findMdian(x, y, low, high);
else
return findMdian(y, x, low, high);
}
/**
* @param x 有序数组x
* @param y 有序数组y
* @param n 两数组长度均为n
* 在x中发现位于两数组的中位数,其中,x,y是有序的
* 使用二分法在数组x中查找中位数
*/
public int findMdian(int []x,int []y,int low,int high)
{
if(low>high)
return -1;
else
{
int len=x.length;
int mid =(low+high)/2;
if(mid==high&&y[0]>=x[mid])
return x[mid];
if(x[mid]>=y[len-mid-2]&&x[mid]<=y[len-mid-1])
return x[mid];
else if(x[mid]<y[len-mid-1])
return findMdian(x, y, mid+1, high);
else
return findMdian(x, y, low, mid-1);
}
}
5.带权中位数
带权中位数,对于排好序的数组,若果这个数前面所有数的权值之和为<=1/2,后面所有数权值之和也是<1/2则,这个数就是带权中位数。
若达到带权中位数首先对n个不同的元素进行排序,时间复杂度为O(nlgn)
然后从第1个元素开始,对权重进行累加,直到找到第一个(权重之和满足为一半的数),则这个数就是所求的带权中位数
这个方法和线性时间内求中位数算法RONDOM-SELECT类似,只是在后处理的时候有些区别,整个过程如下:<span style="font-size:18px;"> RANDOM-WEIGHT-SELECT(A, p, r, w0)
if p = r
then return A[p]
q ← RANDOMIZED-PARTITION(A, p, r)
<span style="color:#FF0000;"> wl ← 0 //这里改成了求其权重
for i ← p to q
do wl ← wl + w[i]
if w0 = wl
then return w[q] </span>
elseif wo < wl
then return RANDOM-WEIGHT-SELECT(A, p, q-1, w0)
else return RANDOM-WEIGHT-SELECT(A, q+1, r, w0-wl) </span>
总结:快排,最大子数组的这种分治思想,分析结果可能出现在哪个子部分,或者交叉出现,再利用他们的性质判断 ,灵活分治。