算法第二章上机实践报告
一、实践题目名称:
找第k小的数
二、问题描述:
设计一个平均时间为O(n)的算法,在n(1<=n<=1000)个无序的整数中找出第k小的数。
提示:函数int partition(int a[],int left,int right)的功能是根据a[left]~a[right]中的某个元素x(如a[left])对a[left]~a[right]进行划分,划分后的x所在位置的左段全小于等于x,右段全大于等于x,同时利用x所在的位置还可以计算出x是这批数据按升非降序排列的第几个数。因此可以编制int find(int a[],int left,int right,int k)函数,通过调用partition函数获得划分点,判断划分点是否第k小,若不是,递归调用find函数继续在左段或右段查找。
输入格式:
输入有两行:
第一行是n和k,0<k<=n<=10000
第二行是n个整数
输出格式:
输出第k小的数
输入样例:
在这里给出一组输入。例如:
10 4
2 8 9 0 1 3 6 7 8 2
输出样例:
在这里给出相应的输出。例如:
2
三、算法描述
这道题其实就是利用快速排序的思想,利用分治法,首先将这个问题进行分解,再递归求解,又因为是寻找第k小的数,所以不用进行合并。根据提示其实不需要完全排好序,而是边排边找。首先在find里面调用partition将a[left:right]进行划分,并返回基准元素x的位置。再将x跟k比较,如果x比k小,说明第k小的数在右边,就继续递归调用find在右边进行查找;反之,说明第k小的数在左边,就调用find在左边进行查找。
四、算法时间及空间复杂度分析
算法时间其实跟快速排序是差不多的,在最好情况下 T(n) = 2T(n/2) + O(n) = O(nlogn),在最坏情况下 T(n) = T(n-1) + O(n) = O(n^2)。空间复杂度是O(n)。
五、代码:
1 #include<iostream>
2
3 using namespace std;
4
5 int partition(int a[], int left, int right)
6 {
7 int x = a[left];
8 int i = left + 1, j = right;
9 while(true)
10 {
11 while(a[i]<x && i<j)
12 i++;
13 while(a[j]>x)
14 j--;
15 if(i >= j)
16 break;
17 int t = a[i];
18 a[i] = a[j];
19 a[j] = t;
20 }
21 a[left] = a[j];
22 a[j] = x;
23 return j;
24 }
25
26 int find(int a[], int left, int right, int k)
27 {
28 int x = partition(a, left, right);
29 if(x == k-1)
30 return a[x];
31 else if(x < k-1)
32 return find(a, x+1, right, k);
33 else
34 return find(a, left, x-1, k);
35 }
36
37 int main()
38 {
39 int n, k, a[10001];
40
41 cin >> n >> k;
42 for(int i=0; i<n ;i++)
43 {
44 cin >> a[i];
45 }
46
47 cout << find(a, 0, n-1, k);
48
49 return 0;
50 }
六、心得体会
这道题就是利用快速排序的算法,一开始就错在了x和k的比较上,因为数组是从0开始,所以要跟k-1比较。