查找并输出数组中第i小的元素,这样的题目我们可以先对数组进行排序,然后输出相对应的第i小的元素;还有另外一种方法,一种解决选择问题的分治算法,该算法是以快速排序算法为模型的,与快速排序一样,我们仍然将输入数组进行划分,但与快速排序不同的是,快速排序会递归处理划分的两边,而该选择方法select只处理划分的一边。这一差异会在性能分析中体现出来:快速排序的期望运行时间为O(nlog(n)),而select的选择期望运行时间是O(n)。
1、对数组进行排序,然后输入第i小的元素,这里排序算法用的是插入排序。
#include <iostream>
using namespace std;
int select(int a[], int n, int i);
int main(void)
{
int a[5] = {4, 67, 8, 2, 3};
cout<<select(a, 5, 4)<<endl;
return 0;
}
static void InsertSort(int a[], int n)
{
int i, j;
for(i = 1; i < n; i++)
{
int x = a[i];
for(j = i; j > 0 && a[j-1] > x; j--)
a[j] = a[j-1];
a[j] = x;
}
}
int select(int a[], int n, int i)
{
InsertSort(a, n);
return a[i-1];
}
2、递归进行选择数组中第i小的元素,select函数运行过程如下:刚开始检查递归的基本情况,当a[]包含只有一个元素时,i必须等于1,直接返回。其他情况下,就会调用Partition函数,将数组划分为两个子数组(可能为空的),a[left..j-1]和a[j+1..right],使得a[left..j-1]中每个元素都小于等于a[j],而a[j]小于a[j+1..right]中的每个元素。与快速排序一样,我们称a[j]为主元,接着计算子数组内a[left..j]中的元素个数k,即处于划分的低区的元素个数加1。然后检查a[j]是否是第i小的元素,如果是接下来直接返回,否则的话确定第i小的元素落在a[left..j-1]和a[j+1..right]两个子数组中的哪一个。如果i<k,则要查找的元素落在了划分的低区,则接下来在低区的子数组中继续递归查找;如果i>k,则要查找的元素落在了高区。我们已经知道了有k个值小于aleft..right]中的第i小的元素,即a[left..j]内的元素,所以我们要找的元素必然是a[j+1..right]中的第I-k小的元素,再进行递归查找。
#include <iostream>
using namespace std;
int select(int a[], int left, int right, int i);
int main(void)
{
int a[5] = {4, 67, 8, 2, 3};
cout<<select(a, 0, 4, 3)<<endl;
return 0;
}
static void Swap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
static int Partition(int a[], int left, int right)
{
int t, i, j;
t = a[right];
i = left;
for(j = left; j < right; j++)
{
if(a[j] < t)
Swap(a[i++], a[j]);
}
Swap(a[i], a[right]);
return i;
}
int select(int a[], int left, int right, int i)
{
if(left == right)
return a[left];
int j = Partition(a, left, right);
int k = j - left + 1;
if(k == i)
return a[j];
else if(i < k)
return select(a, left, j-1, i);
else
return select(a, j+1, right, i-k);
}
1、《算法导论》(第三版)第9章-中位数和顺序统计量