一,实践题目名称:找第k小的数
二,问题描述:设计一个平均时间为O(n)的算法,在n(1<=n<=1000)个无序的整数中找出第k小的数。
三,算法描述: 由三个函数组成:主函数,partition函数,find函数。
代码:
#include <iostream>
#include<stdlib.h>
using namespace std;
#include<stdlib.h>
using namespace std;
int partition(int a[],int left, int right){
int r = rand() % (right - left ) + left; //预防最坏情况
swap(a[r], a[left]); //将找到的数放置在最左侧
int i = left, j = right + 1;
int x = a[left];
while (true)
{
int r = rand() % (right - left ) + left; //预防最坏情况
swap(a[r], a[left]); //将找到的数放置在最左侧
int i = left, j = right + 1;
int x = a[left];
while (true)
{
//将小于x的换到左边,大于x的换到右边
while (a[++i] < x&&i < right);
while (a[++i] < x&&i < right);
while (a[--j] > x);
if (i >= j)break;
swap(a[i], a[j]);
}
a[left] = a[j];
a[j] = x;
return j; //返回中值下标
}
if (i >= j)break;
swap(a[i], a[j]);
}
a[left] = a[j];
a[j] = x;
return j; //返回中值下标
}
int find(int a[], int left, int right, int k) {
if (left == right) return a[left];
int i = partition(a, left, right);
int j = i - left + 1;
if (k <= j) return find(a, left, i, k); //在左边找
else return find(a, i + 1, right, k - j); //在右边找
}
int main()
{
int n;
cin >> n;
int m;
cin >> m;
int a[n];
for (int k = 0; k < n; k++)
{
cin >> a[k];
}
cout << find(a, 0, n - 1, m);
{
int n;
cin >> n;
int m;
cin >> m;
int a[n];
for (int k = 0; k < n; k++)
{
cin >> a[k];
}
cout << find(a, 0, n - 1, m);
return 0;
}
}
四,时间空间复杂度分析
最坏情况下,每次划分点为最小值或最大值,即划分区域包含n-1和1个元素时,由于partition计算时间为O(n),n>1时,T(n-1)+O(n)的解为O(n^2);最好情况下,每次取得的基准为中值,即划分产生两个大小为n/2的区域,n>1时,T(n)=2T(n/2)+O(n)的解为O(nlogn)。
最坏情况下,每次划分点为最小值或最大值,即划分区域包含n-1和1个元素时,由于partition计算时间为O(n),n>1时,T(n-1)+O(n)的解为O(n^2);最好情况下,每次取得的基准为中值,即划分产生两个大小为n/2的区域,n>1时,T(n)=2T(n/2)+O(n)的解为O(nlogn)。
最好情况下空间复杂度为O(logn),最坏情况下需递归n-1次,空间复杂度为O(n)。
五,心得体会
用递归法解题使得对递归有了更深的理解。
在编程中,值得注意的是,partition函数里的小细节,即大于小于符号,判断数字是否超出界限等。