找出乱序数组中第k小的数字

package demo;

public class P39 {
	//找出乱序数组中第k小的数字
	//思路:利用快排中的分区方法,下标为a-1的主元就是第a小的数字,比较a和k,再到k在的那一边用分区找
	//这样比快排更优,因为分区过程中排除了没用的部分
	public static void main(String[] args) {
		int[] arr= {3,9,7,6,1,2};
		System.out.println(selectK(arr, 0, arr.length-1, 1));
		System.out.println(selectK(arr, 0, arr.length-1, 2));
		System.out.println(selectK(arr, 0, arr.length-1, 3));
		System.out.println(selectK(arr, 0, arr.length-1, 4));
		System.out.println(selectK(arr, 0, arr.length-1, 5));
		System.out.println(selectK(arr, 0, arr.length-1, 6));
	}
	
	static int selectK(int[] arr, int s,int e,int k) {
		int a=partition2(arr, s, e);		//主元下标
		int aK=a-s+1;		//主元是第几个元素
		if(aK==k)
			return arr[a];
		else if(aK>k)
			return selectK(arr, s, a-1, k);		//k在左区
		else
			return selectK(arr, a+1, e, k-aK);		//k在右区,因为丢掉了aK个比它小的元素,k在子问题中是第(k-aK)小
	}

	
	static int partition2(int[] arr,int p,int r) {		//双向扫描
		//优化,三点中值法,取头、中、尾三点的中值作为主元
		int mid=p+((r-p)>>1);
		int midValue;		//中值的下标
		int temp;
		
		if(	 (arr[mid]-arr[p])*(arr[mid]-arr[r])<0  ) {
			midValue=mid;
		}
		else if(	 (arr[p]-arr[mid])*(arr[p]-arr[r])<0  ) {
			midValue=p;
		}
		else {
			midValue=r;
		}
		temp=arr[p];			//把中值交换到主元位置
		arr[p]=arr[midValue];
		arr[midValue]=temp;
		
		int value=arr[p];		//把p作为主元
		int left=p+1;		
		int right=r;
		
		while(left<=right) {		//左右交错为结束条件,此时右指针指向左区末尾,左指针指向右区开头
			while(left<=right && arr[left]<=value)	left++;		//让左指针指向第一个大于主元的元素
			while (left <= right && arr[right] > value)
				right--; // 让右指针指向 从右边数第一个 小于等于主元的元素
			if (left < right) {		//交换值
				temp = arr[left]; 
				arr[left] = arr[right];
				arr[right] = temp;
			}
		}
		arr[p]=arr[right];
		arr[right]=value;
		
		return right;
	}
	
	
}
posted @   fighterk  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
点击右上角即可分享
微信分享提示