算法第二章——上机实践报告
设计一个平均时间为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
△算法描述:(按题目所示采用的两个基本算法——如下↓)
1 int partition(int a[],int left,int right) 2 { 3 int i = left; 4 int j = right + 1; 5 int x = a[left]; 6 //将<x的元素交换到左边区域 7 //将>x的元素交换到右边区域 8 while(true){ 9 while(a[++i] < x && i < right); 10 while(a[--j] > x); 11 if(i >= j) break; 12 swap(a[i],a[j]); 13 } 14 swap(a[left],a[j]); 15 a[j] = x; 16 return j; 17 } 18 19 20 int find(int a[],int left,int right,int k) 21 {//返回第k小的数 22 int q = partition(a,left,right);//获取划分点 23 if(k-1 == q){ 24 cout << a[k-1];//找到划分点直接输出 25 } 26 else if(k-1 < q){ 27 find(a,left,q,k);//对左半段寻找 28 } 29 else 30 find(a,q+1,right,k);//对右半段寻找 31 return 0; 32 33 34 }
Tips:1.使用快速排序的方法,在patition函数中找到一个确定的基准元素a[left]对子数组a[left:right]进行划分,以此得到划分点传递给find函数,
来判断划分点与k值的大小来决定在左边还是右边比大小或直接返回划分点的值。
2.find函数主要用于判断k值与划分点是否相同,以及不同时对划分点的左右半段各进行的递归查找操作。
△算法时间及空间复杂度
①时间复杂度(O(nlogn)):主要根据n值区分出具体情况中的时间复杂度,每次都划分成两个序列;
当n <= 1时,T(n)= O(1);
当n > 1时,T(n)= 2T(n / 2)+ O(n);
根据T(n)= aT(n/b)+ O(n^d)可知 a = 2; b = 2-------> n^log22 = n;
故算法的时间复杂度为T(n) = O(nlogn)。
②空间复杂度:O(n)(递归调用)。
△心得体会:
在实验室听完老师讲授之后在面对该实践题目的时候对整道题的思路清晰了很多,面对无序数组时采用哪种排序方法更高效节省空间与时间,或者如何安排算法步骤都逐渐在脑子里有了点架构。刚开始代码一直是答案错误,发现是自己把find函数想复杂了,其实关键就是看k值与划分点的比较。同时在find里比较时要注意数组是从0开始的,所以比较下标值和k值时,k要-1。希望自己能逐渐掌握分治法策略的思想来做题。