算法第二章上机实践报告
1.实践题目:2-1 找第k小的数
2.题目描述:
设计一个平均时间为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
3.算法描述:
#include <iostream>
using namespace std;
void swap(int a[],int &x,int &y){
int z=x;
x=y;
y=z;
}
int partition(int a[],int left,int right){
int x=a[left];
int i=left+1;
int j=right;
while(i<=j){
while(i<=right&&a[i]<=x) i++;
while(a[j]>x) j--;
if(i>j) break;
swap(a[i],a[j]);
}
swap(a[left],a[j]);
return j;
}
int find(int a[],int left,int right,int k){
int p = partition(a,left,right);
if(p==k-1){
return a[k-1];
}
else{
if(p>k-1){
return find(a,left,p-1,k);
}
else{
return find(a,p+1,right,k);
}
}
}
int main(){
int n,k;
cin >> n >> k;
int *a = new int [n];
for(int i=0;i<n;i++){
cin >>a[i];
}
cout << find( a,0,n-1, k);
return 0;
}
<1>这里用的是快速排序的思想:先分解找划分点,然后递归求解,最后合并。
<2>这里需要用到交换,所以要设置一个swap来实现交换的目的,其中变量名不能重复,如果数组是a,那么就不能是int &a。
<3>题目的提示中说可以借助两个函数:int partition(int a[],int left,int right)和int find(int a[],int left,int right,int k);partition函数是用来将小于x的元素放在原数组的左半部分,将大于x的元素放在原数组右半部分;find函数可以调用partition函数来找到划分点,在来判断划分点与k值的大小来决定在那边比大小或直接返回划分点的值。
<4>因为数组的下标从0开始,所以在比较数组下标(划分点的下标)和k值大小的时候,是比较下标值和k-1的大小,若二者相等,则直接返回;若下标值>k-1,则说明所需的值在数组的左边,则需要在左边寻找;否则在右边寻找。
<5>在partition函数里面,要先设置一个x为划分点,一般令其等于原数组最左边的那个元素的值;然后在设置两个变量,一个i一个j,令其分别为left+1和right,就从两边开始往中间走;如果i>x&&j<x,则需交换其二者的位置;最后在i和j相遇时,要交换i和j的值,说明程序到了中间,此时还要交换划分点和j下标的两个值,即让划分点下标的元素到其该在的位置;最后返回划分点下标的值。
4.算法时间以空间复杂度分析
时间复杂度(题目有规定):T(n)=O(n)
空间复杂度:O(1) (我觉得借助的临时变量与n的规模无关)
5.心得体会:
<1>利用快速排序的时候就是用partition函数,要注意先设置一个划分点。
<2>在partition函数里面一开始的while语句的条件是i<=j,说明数组是存在的;在i++这个while语句里面是要有两个条件的,i<=right&&a[i]<=x,要二者皆满足才行,不然会超出数组范围;而j--的while语句里就只有一个条件,只需a[j]>x,不用加j>=left,因为已经有个x守在那里了,不会超出范围。
<3>跳出循环的条件要弄清楚,是i>j。
<4>要注意数组是从0开始的,所以比较下标值和k值时,k要-1。
<5>在左边寻找或右边寻找的时候,find(a,left,p-1,k)和find(a,p+1,right,k)中的k都是针对于整个数组a而言的,所以右边寻找的时候不用写成k-p,直接写k即可。