线性时间选择算法

期望为线性时间的随机选择算法

期望为线性时间的随机选择算法 是一种解决选择问题的分治算法。它是以快速排序算法为模型的,与快速排序算法一样,仍然将输入数组进行递归划分;但,与快速排序算法不同的是,快速排序算法需递归处理划分的两边,而选择算法则是只处理一边。这一差异导致其性能上有所区别:快速排序算法的期望运行时间为 Θ(nlgn),而选择算法的期望运行时间为 Θ(n)。

代码如下:

    //期望为线性时间的选择算法
	public static int randomizedSelect(int[] array,int p,int r,int i) {
		if(i > array.length) {
			return -1;
		}
		if(p == r) {
			return array[p];
		}
		int q = randomized_partition(array,p,r);
		int k = q - p + 1;
		if(i == k) {
			return array[q];
		}else if(i < k) {
			return randomizedSelect(array,p,q-1,i);
		}else {
			return randomizedSelect(array,q+1,r,i-k);
		}
	}
	//分区操作
	private static int randomized_partition(int[] array, int start, int end) { 
		int pivot = (int) (start+Math.random()*(end-start+1));
	    int smallIndex = start - 1;
	    swap(array, pivot, end);
	    for (int i = start; i <= end; i++)
	    	if (array[i] <= array[end]) {
	    		smallIndex++;
	            if (i > smallIndex)
	            	swap(array, i, smallIndex);
	        }
	    return smallIndex;
	}
	//交换数组元素位置
	private static void swap(int[] array, int i, int j) {
		int temp = array[i];
	    array[i] = array[j];
	    array[j] = temp;	
	}

然而,由于划分操作randomized_partition的局限性,该选择算法的最坏情况运行时间为Θ(n2)。下面,将介绍一种改进的选择算法。

最坏情况为线性时间的选择算法

最坏情况为线性时间的选择算法randomizedSelect 一样,select 算法通过对输入数组的递归划分来找出所需元素,但是,在划分的过程中 select 算法总能保证得到对数组的一个“好”的划分。select 使用的也是来自快速排序的确定性划分算法 partition ,但做了修改------把划分的主元也作为输入参数。

通过select算法,可确定一个有n>1个不同元素的输入数组中第p大的元素。

select 算法步骤如下:
(1) 将输入数组的n个元素划分为 group=(n+4)/5 组,每组5个元素,且最多只有1组由剩下的 n%5 个元素组成。
(2) 寻找这group组中每一组的中位数(通过对每组元素进行插入排序,然后确定每组有序元素的中位数),并将每组的中位数移至输入数组的前排位置(便于后续处理)。
(3) 对第二步中找出来的group个中位数,递归调用select以找出其中位数mid(如果有偶数个中位数,为了方便,约定mid是下中位数)
(4) 通过修改后的 partition 算法,把中位数的中位数mid作为主元对输入数组进行划分。
(5) 若 p=k (p是 partition 返回值,k是要选择的第k大的元素),则返回 a[p] ;若 p<k ,则在低区递归调用select查找第k大的元素;若 p>k ,则在高区递归调用select查找第 p-k 大的元素。

代码如下:

    //最坏情况为线性时间的选择算法
	public static int select(int[] a,int l,int r,int k){
		if(k > a.length) {
			return -1;
		}
	    if(r-l <= 4){
	        insertionSort(a, l, r);    //用简单插入排序进行排序
	        return a[l+k-1];
	    }
	    int group = (r-l+5)/5;
	    for(int i = 0;i<group;i++){
	        int left = l+5*i;
	        int right = (l+i*5+4) > r ? r : l+i*5+4; 
	        int mid = (left+right)/2;
	        insertionSort(a, left, right);
	        swap(a, l+i, mid);     //把各组中位数移至数组前排
	    }
	    int pivot = select(a,l,group-1,(group+1)/2);  //找出中位数的中位数
	    int p = partition(a,l,r,pivot);    //用中位数的中位数作为基准的位置
	    k = k-1;
	    if (k == p)
	        return a[p];
	    else if (k < p)
	        return select(a, l, p-1, k);
	    else                    
	        return select(a, p+1, r, k-p);
	}
	
	private static int partition(int[] array, int start, int end,int pivot) { 
	    int smallIndex = start-1;
	    swap(array, pivot, end);
	    for (int i = start; i <= end; i++)
	    	if (array[i] <= array[end]) {
	    		smallIndex++;
	            if (i > smallIndex)
	            	swap(array, i, smallIndex);
	        }
	    return smallIndex;
	}
	
	public static void insertionSort(int[] array,int l,int r) { 
		int temp;
		for(int j = l+1; j <= r; j++) {
			temp = array[j];
			int i = j-1; 
			while(i >= l && array[i] > temp) { 
				array[i+1] = array[i];
				i--;
			}
			array[i+1] = temp;
		}
	}

测试代码:

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] a = new int[] { 1, 1, 1, 5, 6, 22, 44, 2, 11, 66 };
		int aim = 8;
		
		System.out.println("=====================");
		
		int res = algorithm.randomizedSelect(a, 0, a.length-1, aim);
		System.out.println(res);
		System.out.println("=====================");
		
		int ress = algorithm.select(a, 0, a.length-1, aim);
		System.out.println(ress);

		//输出均为 22
	}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值