第K小元素 时间复杂度n

参考视频bilibil fjnuzs


一、题目分析

给出一个n个元素的序列,求其中的第k小元素(即序列按升序排序后的第k个元素)

如果用排序时间复杂度nlogn,随机快速排序时间复杂度n。

但是这里采用一种分治的方法使时间复杂度为n。若分治算法中,每次分解子问题后,只取其中一个子问题解之,丢弃其余的子问题。每次递归调用,问题的规模以常数因子被减小。设分解问题只需线性时间,则算法的时间复杂性满足 Θ n \Theta n Θn

二、计算思路伪代码

大致思路是选一个“主元”,比他大的放一个数组,比他小的放另一个数组,等于的再放一个数组。然后每个数组都有长度,子问题就变为k所在的那个数组,其余两数组便可以舍去。直到只有44个元素时就去排序找第K。(为啥是44哩,是在计算时间复杂度时确定的)
还有一个难度是找到“主元”就是一个中间的数,避免子问题太大,被舍弃的内容太少。找主元的方式是五个一组,不够5个舍弃掉,将每五个的中间数放入mid数组,再从mid数组里找一个中间数作为主元(mm)。

//求A[low.high]中的第k小元素并返回。
select ( A, low, high, k )  
	p=high-low+1  			//p为当前处理的元素个数。
	if p<44 then 			// 当元素个数<44时,直接求解。
		将A[low..high]排序
		return(A[low+k-1])
	end if
	
//以下分解子问题
	q= p/5下取整
	将A[low.low+5q-1]分为q组,每组5个元素
	将q组中的每一组单独排序并找出中项,所有的中项存于数组Mid[1..q]中
	mm=select(Mid , 1, q, q/2上取整)        //求中项序列M的中项mm
	将A[low..high]分成三组
	A1={a|a<mm} , A2={a|a=mm}, A3={a|a>mm}
	
//以下选择一个子问题递归或直接解。
	case
		|A1|>=k: return select (A1,1, |A1|, k)
		|A1|+|A2|>=k: return mm
		|A1|+|A2|<k: return select (A3,1, |A3|, k-|A1|-|A2| )
	end case
end select

三、Java实现

但是有一个不足,就是我测试的数据没有找到大于44的(lan)所以只能算跟着伪代码码了一遍。

public class TestMink {

	public static void main(String[] args) {
		int[] arr = {56, 34, 22, 7, 16, 95, 46, 37, 81, 12, 73, 26, 19, 31, 68, 42, 3, 72, 51};
		System.out.println(select(arr, 0, arr.length-1, 8));
	}
	
	public static int select(int[] arr, int begin, int end, int k) {//传的是下标
		int p = end-begin+1;
        if (p < 44) {
        	Arrays.sort(arr);
            return arr[begin+k-1];
        }
        
        int q = (int)Math.floor(p/5);
        int mm = medians(arr, begin, q);//起始下标和一共多少组
        
        int[] a1 = new int[end-begin+1];
        int[] a2 = new int[end-begin+1];
        int[] a3 = new int[end-begin+1];
        int count1 = 0;
        int count2 = 0;
        int count3 = 0;
        for(int i=begin; i<=end; i++) {
        	if(arr[i] < mm) {
        		a1[count1++] = arr[i];
        	}else if(arr[i] > mm) {
        		a3[count3++] = arr[i];
        	}else {
        		a2[count2++] = arr[i];
        	}
        }
        if(count1 >= k ) {
        	return select(a1, 0, count1, k);
        }else if(count1+count2 >= k) {
        	return mm;
        }else {
        	return select(a3, 0, count3, k-count1-count2);
        }
    }
	
	//找主元
	public static int medians(int[] arr, int low, int q) {
        int[] mArr = new int[q];
        int j = 0;
        int count = 0;
        int[] temp = new int[5];
        for (int i = low; i < low+5*q-1; i++) {
        	if(count == 5) {
        		count = 0;
        		Arrays.sort(temp);
        		mArr[j++] = temp[2];
        	}else {
        		temp[count++] = arr[i];
        	}
        }
        return select(mArr, 0, mArr.length - 1, mArr.length / 2);
    }
}


四、时间复杂度

select函数两个地方被调用,在找主元时递归的规模是n/5,还有就是舍弃两个数组递归去解决子问题时。a1和a3的规模采用估算。
在这里插入图片描述
T ( n ) ⩽ { c , n < 44 T ( ⌊ n / 5 ⌋ ) + T ( ⌊ 3 n / 4 ⌋ ) + c n , n ≥ 44 T(n) \leqslant \begin{cases} c &,n < 44\\ T(\lfloor n/5 \rfloor) + T(\lfloor 3n/4 \rfloor)+cn &,n\ge44 \end{cases} T(n){cT(n/5)+T(3n/4)+cn,n<44,n44
解的 T ( n ) ⩽ 20 c n T(n)\leqslant20cn T(n)20cn

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值