在O(n)的时间复杂度内查找第K大的元素

这道题目其实是快速排序算法的一个延伸,如何做到在O(n)的时间复杂度内查找数组内第K大的元素?

学习过快速排序就知道,快排每次分区会把数据分为三个部分,a[0,p-1],a[p]以及a[p+1,n-1]三个区间。每次分区我们都将大于分区点元素放置在分区元素左边,小于分区元素的统一放到分区元素右边,那么这样一来,我们就可以利用快速排序实现从大到小的数组排序了。但是,这里说这个不是为了排序,而是想说一个关于从大到小排序后数组的一些规则:

1 如果P+1=k,那么a[P]就是要找的元素。

2 如果P+1<k,那么要继续在a[p+1,n-1]之间找。

3 如果P+1>k,那么要继续在a[0,p-1]之间找。

我们要查找第K大的数据,P作为分区点,那么利用以上规则就可以找到这个第K大元素,如果你还不明白,看下下边这个图:

这个图示我要查第K大元素,k=4时的图解过程。可以看出来每次分区我都可以把范围缩小。我只需要在这个小范围内找就可以了。

时间复杂度如何推导呢?

第一次分区,我需要对大小为n的数组执行分区操作,需要遍历n个元素。第二次分区查找,只需要对n/2个数据执行分区,只需要遍历n/2个数组,当然这里说的都是平均情况。依次类推,分区遍历的元素个数为n/2、n/4、n/8一直到区间为元素为1为止。把每次分区遍历的元素个数加起来:n+n/2+n/4+n/8...+1,最终等比数列求和的结果就是2n-1,所以时间复杂度为O(n).

代码如下:

package com.study.algorithm.array;

/**
 * 在O(n)的时间复杂度内查找第K大的元素
 * @author Administrator
 *
 */
public class FindNData {

	
	/**
     * 快速排序
     * @param arr
     * @param n
     */
    public static void quickSort(int[] arr,int n,int k){
 
        quickSort(arr,0,n-1,k);
 
    }
 
    /**
     	* 根据分区点,递归继续分解子分区
     * @param arr
     * @param p
     * @param r
     */
    public static void quickSort(int[] arr,int p,int r,int k){
        if(p>=r){
        	System.out.println("第"+k+"大元素为:"+arr[p]);
            return;
        }
        int q = partition(arr,p,r);
        if(q+1==k) {
        	System.out.println("第"+k+"大元素为:"+arr[q]);
        	return;
        }else if(q+1<k){
        	 quickSort(arr,q+1, r,k);
        }else if(q+1>k) {
        	quickSort(arr,p,q-1,k);
        }
       
    }
 
    /**
     * 快排分区
     *随机生成[p,r]区间内pivot,将比pivot大的放在其右侧,比pivot小的放在其左侧
     * @param arr
     * @param p
     * @param r
     */
    public static int partition(int[] arr,int p,int r){
        int pivot = arr[r];
        int i=p;
        for (int j = p; j < r; j++) {
            //当前元素比分区点小,则交换当前元素arr[j]到arr[i],也就是将小的移动到左侧,大的移动到右侧,而这个大小也是相对于分区点来说的,将来分区点会放到中间位置
            if(arr[j] > pivot){
                int temp = arr[j];
                arr[j] = arr[i];
                arr[i] = temp;
                //交换完位置(每确定完一个小元素后)以后,移动i指针到下一位(这个位置也是为下一个小元素准备的),只要arr[j]比分区点小,就将其交换到指针i的位置,并将i后移
                i++;
            }
        }
        //迭代完成后,所有相对于pivot小的元素都被移动到靠左的位置(i指针动态指向的位置),所有相对于pivot大的元素都被移动到右侧,但是还是需要pivot将大小区间分割开
        int temp = arr[i];
        arr[i]=arr[r];
        arr[r]=temp;
        return i;
    }

	public static void main(String[] args) {
		int[] arr= {6,1,3,5,7,2,4,9,11,8};
		int k=4;
		quickSort(arr,10,k);
	}

}

执行结果:

第4大元素为:7

 

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jeff.sheng

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值