算法第二章上机实践报告

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即可。

posted @ 2020-10-07 21:31  虾呀  阅读(120)  评论(0编辑  收藏  举报