舍伍德算法解决线性时间选择

一、需求分析

0.问题描述

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

1.问题分析

  • 对于选择问题而言,用拟中位数作为划分基准可以保证在最坏的情况下用线性时间完成选择。
  • 如果只简单地用待划分数组的第一个元素作为划分基准,则算法的平均性能较好,而在最坏的情况下需要O(n^2)计算时间。
  • 舍伍德选择算法则随机地选择一个数组元素作为划分基准,这样既保证算法的线性时间平均性能,又避免了计算拟中位数的麻烦。

3.输出数据

分析并阐述要输出的信息和数据(结果)

4.测试样例设计

针对问题的功能,设计5组有代表性的测试样例,说明每组测试样例的测试目的

算法设计与分析

1.算法的基本思想

类似于快排的一种思想。

  • 随机选择一个基准值pivot,将基准值与范围左侧元素交换位置。
  • 以基准值为轴进行划分,先从右往左找到一个位置j的元素小于pivot,然后从左往右找到一个位置i的元素大于pivot,交换两个元素的位置。
  • 当i==j时停止交换,这时j的左侧元素小于等于pivot,右侧元素大于等于pivot、
  • 因为j处的元素肯定小于等于pivot,将L与j处元素交换位置,j位置的元素就是这个序列里面第 j - L+1小的元素。
  • 如果j - L +1==k,则问题解决
  • 否则判断k与j - L +1的相对大小,当j - L +1< k时继续对右侧的序列重复以上过程,否则对左侧序列重复以上过程,直到找到满足条件的j - L +1

2.输入和输出的格式

  • 从文件输入,输出到文件。
  • 输入第一行是元素个数n和要求的元素的位置k
  • 第二行是n个元素的值

3.算法的具体步骤

4.算法的时空分析

①时间复杂度:

  • ①时间复杂度:

    划分基准是随机的。可以证明,当用算法Select对含有n个元素的数组进行划分时,划分出的低区子数组中含有一个元素的概率为2/n,含有i个元素的概率为1/n (i=2, 3,.,. n-1)。

    设T(n)是算法Select作用于一个含有n个元素的输入数组上所需的期望时间的上界,且T(n)是单调递增的。在最坏情况下,第k小元素总是被划分在较大的子数组中。得到关于T(n)的递归式

         

  • 解递归式得到结果:O(n)

    ②空间复杂度:

    存储元素的值,O(n)

②空间复杂度:

存储元素的值,O(n)

三、测试结果

 

5 2
5 9 1 4 6

10 8
19 2 56 1  3 5 3 2 7 9 

全部代码:

#include <iostream>
#include <time.h> 
#include <stdlib.h>
#include <fstream>
using namespace std;
ifstream in("input.txt");
ofstream out("output.txt");

//内联交换函数 
template <class Type>
inline void Swap(Type& a, Type& b){
	Type temp = a;
	a = b;
	b = temp;
}
 
template<class Type>
Type select(Type a[], int l, int r, int k){
	while (true){
		if (l >= r){
			return a[l];
		}		
		//随机选择划分基准
		int i = l, j = l + rand() % (r - l + 1);
		Swap(a[i], a[j]);
		j = r + 1;
		Type pivot = a[l];
		//以划分基准为轴做元素交换
		while (true){
			while (i<r&&a[++i] < pivot);
			while (j>l&&a[--j] > pivot);
			if (i >= j){
				break;
			}
			Swap(a[i], a[j]);
		}
		//如果最后基准元素在第k个元素的位置,则找到了第k小的元素 
		if (j - l + 1 == k){
			return pivot;
		}	
		//a[j]必然小于pivot,做最后一次交换,满足左侧比pivot小,右侧比pivot大
		a[l] = a[j];
		a[j] = pivot;		
		//对子数组重复划分过程
		if (j - l + 1 < k){
			k = k - (j - l + 1 );//基准元素右侧,求出相对位置 
			l = j + 1;
		}else{//基准元素左侧 
			r = j - 1;
		}
	}
}

template <class Type>
Type Select(Type a[],int n,int k){
	if(k<1||k>n){
		printf("Index out of bounds\n");
		exit(0);
	}
	return select(a,0,n-1,k);		
}

int main(){
	int n,k,r;
	while(in>>n){
		in>>k;
		int a[n];
		for(int i=0;i<n;i++)
			in>>a[i];
		r=Select<int> (a,n,k);
		out<<r<<'\n';
	}
	in.close();
	out.close();
	return 0; 
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hnu哈哈

请接受直女的么么哒????

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

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

打赏作者

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

抵扣说明:

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

余额充值