1.问题
运用特定分治策略在一组数中选出第k小元素
2.解析
先在这组数中找出一个数m,以m为标准对这个数组进行拆分,将比m小的数都放入S1中,比m大的数都放入S2中,设一个数k=|S1|+1。
- 若k=|S1|+1,那么当前数组的中位数就是要找的第k小的数。
- 若k<=|S1|,那么当前的问题就可以归纳为在S1中找到第k1小的问题,此时k1=k。
- 若k>|S1|+1,当前问题就可以归纳为在S2中找到k2位置上的书的问题,此时k2=k-|S1|-1.
具体实现方法为:将数组分为五个一组,最后不够5个就单列一组,对每一小组进行排序找出每组的中位数,将每个小组的中位数取出,重复这个操作,直至只剩最后一个数字。以这个数字作为m将小于m的数全部放到其左边,大于m的值全部放到其右边,再将此时m的下标与k做对比,若此时m*的下标大于k,则对其左边的数字继续重复之前的动作,若大于k则对右半部分重复,依次递归,直至找到第k小的数。
3.设计
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <algorithm>
using namespace std;
void InsertSort(int* a, int left, int right)//对数组进行排序
{
for (int i = left + 1; i <= right; i++)
{
if (a[i - 1] > a[i])
{
int t = a[i];
int j = i;
while (j > left&& a[j - 1] > t)
{
a[j] = a[j - 1];
j--;
}
a[j] = t;
}
}
}
int FindMid(int* num, int left, int right)
{
if (left == right) return left;
int i = 0;
int n = 0;
for (i = left; i < right - 5; i += 5)//对数组排序并将中位数放到最前面
{
InsertSort(num, i, i + 4);
n = i - left;
swap(num[left + n / 5], num[i + 2]);
}
int w = right - i + 1;
if (w > 0)//对剩余部分排序并将中位数放到最前面
{
InsertSort(num, i, i + w - 1);
n = i - left;
swap(num[left + n / 5], num[i + w / 2]);
}
n /= 5;
if (n == left) return left;
return FindMid(num, left, left + n);
}
int Partion(int* num, int left, int right, int p)
{
swap(num[p], num[left]);
int i = left;
int j = right;
int pivot = num[left];
while (i < j)
{
while (num[j] >= pivot && i < j) //找到第一个比pivot小的数字的下标j;
{
j--;
}
num[i] = num[j];
while (num[i] <= pivot && i < j) //找到第一个比pivot大的数字的下标i;
{
i++;
}
num[j] = num[i];
}
num[i] = pivot;
return i;
}
int Select(int* num, int left, int right, int k)
{
int p = FindMid(num, left, right);
int i = Partion(num, left, right, p);
int m = i - left + 1;
if (m == k)
{
return num[i];
}
if (m > k)
{
return Select(num, left, i - 1, k);
}
return Select(num, i + 1, right, k - m);
}
int main()
{
int n, k;
cin >> n;
int* num = (int*)malloc(sizeof(int) * n);//分配空间
for (int i = 0; i < n; i++)cin >> num[i];
cin >> k;
printf("%d\n",Select(num, 0, n - 1, k));
return 0;
}
4.分析
时间复杂度为O(n)