线性时间选择

线性时间选择

题目:

​ 给定线性序集中n个元素和一个整数k(1<=k<=n),要求找出这n个元素中第k小的元素,即如果将这n个元素依其线性序排列时,排在第k个位置的元素即为要找的元素。当k=1时,就是要找最小元素;当k=n时,就是要找最大元素;当k=(n+1)/2时,称为找中位数。

分析:

​ 可以模仿快速排序,与快速排序不同的是,它只对划分出的子数组之一进行递归处理。为了使递归树更加对称,提高算法效率,可以采用快排中的随机分区法 RandomizedPartition 来实现。在执行完随机分区后,原数组 a[p:r] 被划分成两个子数组 a[p:i]a[i+1:r] 。如果 k>i 则说明在右半分区, 如果 k<=i 则说明在左半分区。确定好目标元素所在分区后,仅需对该目标分区进行递归分治即可。

实现:

  1. 边界条件为 left==right 时,说明k所在分区都已排序完成,返回 a[left] 即可。
if(left==right){
    return a[left];
}
  1. 通过随机分区 randomizedPartition 获取基准位置 pivot
// 随机分区
int randomizedPartition(int a[], int left, int right){
	int randPivot = rand() % (right - left) + left;
	swap(a, left, randPivot);
	
	int p = left;
	int pivot = a[left];
	
	while(left < right){
		while(left < right && a[right] >= pivot){
			right--;
		}
		while(left < right && a[left] <= pivot){
			left++;
		}
		swap(a, left, right);
	}
	swap(a, left, p);
	return left;	
}
  1. 通过比较基准位置 pivot 和目标位置 k 的大小,来确定要往哪个分区进一步划分。当 k <= pivot 时往右边部分再次递归;当 k > pivot 时往左边部分再次递归。
if(k <= pivot){
    // 在右边部分
    lineTimeSelect(a, left, pivot, k);
} else {
    // 在左边部分
    lineTimeSelect(a, pivot+1, right, k);
}

完整代码:

#include<stdio.h>
/*
主算法
a: 数组
left: 左边界
right: 有边界
k: 第几个元素
*/
int lineTimeSelect(int a[], int left, int right, int k){
	if(left==right){
		return a[left];
	}
	int pivot = randomizedPartition(a, left, right);
	if(k <= pivot){
		// 在右边部分
		lineTimeSelect(a, left, pivot, k);
	} else {
		// 在左边部分
		lineTimeSelect(a, pivot+1, right, k);
	}
}
// 随机分区
int randomizedPartition(int a[], int left, int right){
	int randPivot = rand() % (right - left) + left;
	swap(a, left, randPivot);
	int p = left;
	int pivot = a[left];
	while(left < right){
		while(left < right && a[right] >= pivot){
			right--;
		}
		while(left < right && a[left] <= pivot){
			left++;
		}
		swap(a, left, right);
	}
	swap(a, left, p);
	return left;
}
// 交换
void swap(int a[], int left, int right) {
	int temp = a[right];
	a[right] = a[left];
	a[left] = temp;
}
void main(){
	int a[10] = {4,7,3,1,2,6,6,9,8};
	int length = sizeof(a) / sizeof(int);
	int ak = lineTimeSelect(a, 0, length-1, 7);
	printf("%d\n", ak);
	return;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值