线性时间选择算法

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>
总结:快排,最大子数组的这种分治思想,分析结果可能出现在哪个子部分,或者交叉出现,再利用他们的性质判断 ,灵活分治。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值