要求:
实践报告任选一题进行分析。内容包括:
- 实践题目名称
- 问题描述
- 算法描述
- 算法时间及空间复杂度分析(要有分析过程)
- 心得体会(对本次实践收获及疑惑进行总结)
实践题目:2-1 找第k小的数 (25分)
问题描述:
设计一个平均时间为O(n)的算法,在n(1<=n<=1000)个无序的整数中找出第k小的数。
算法描述
(快速排序)
函数int partition(int a[],int left,int right):用第一个数作为基准点,将比它小的数放在左侧,比它大的数放在右侧。i从左往右扫,j从右往左扫,在不交叉的情况下(i<j)若是有大数在左边,则与右边的小数交换位置,直到最后交叉,则交换基准点与a[j]的位置。最后返回划分点的位置j
函数find(int a[],int left,int right,int k):调用partition函数获得基准点,将返回的划分点j与k比较。(在此划分点的左边为小数,右边为大数,所以比较即可知道第k小的数的范围。)
算法时间及空间复杂度分析
时间复杂度
partition算法的时间复杂度O(n),最坏情况下,会将n个元素分为n-1与1的组合,此时T(n)=O(n^2);
最好情况下,每次都产生n/2的区域,则此时T(n)=O(nlogn)
空间复杂度
O(n),存放无序数的数组
心得体会
①一开始并没有想到是快速排序的思想,由于是要找第k小,而快速排序刚好满足这一特点,数本身可能无序,但是总有前面数小,后面数大的情况。这正好满足第k小的特点,若元素位置与k-1相等,则说明为要找的数。
②一开始没有考虑到数组下标从0开始,而最小k为1.
代码
#include <iostream> using namespace std; int partition(int a[],int left,int right) { int x = a[left]; int i = left+1; int j = right; int temp; while(i<j) { while(a[i] < x && i<j)
//若是一直比x小,则有越界的可能,所以要加i<j的判断 { i++; } while(a[j] > x) { j--; }
//若是已经交叉了,则要break,停止交换,否则大数会跑到小数的位置 if(i>=j) break; temp = a[i]; a[i] = a[j]; a[j] = temp; }
//基准点与a[j]的交换 a[left] = a[j]; a[j] = x; return j; } int find(int a[],int left,int right,int k) { int x = partition(a,left,right); if(x+1 == k) { return a[k-1]; } if(x+1 < k) { find(a,x+1,right,k); } else find(a,left,x-1,k); } int main() { int n,k; cin >> n >> k; int a[10002]; for(int i=0; i<n; i++) { cin >> a[i]; } cout << find(a,0,n-1,k); }