实验问题:
给定一个序列,求出此序列第k小的元素
问题分析:
可以利用快速排序,随机取一个元素将比它小的数放在它的右边,
比它大的数放在左边。根据左子集的元素个数可以分为三种情况:
第一种:nleft=k-1 那么分界数据即为问题的答案;
第二种:nleft>k-1 那么第k小的数存在于左子集中问题规模减小;
第三种:nleft<k-1 那么第k小的数存在于右子集中,问题变为选择第k-nleft-1小的数。
数学建模:
建立函数get_kSmall(arr,begin,end,k)其中arr是开始输入的数组,begin是数列的首指针,end是数列的尾指针,k是要求的第k小的数。
开始进行快速排序,随机找一个数arr[begin],以这个数为基准,大于此数放右边,小于此数放左边。
最后得到arr[begin]这个数所处在序列的位置i。接着判断i与k-1之间的大小,如果等于则返回arr[i];如果大于,继续在左子集中寻找即get_kSmall(arr,begin,i-1,k);如果小于,继续在右子集中寻找,即get_kSmall(arr,i+1,end,k-i-1)。
递归出口是k<=1或者i=k-1。
实验代码:
//计算序列第K大的元素
//采用分治法(减治法),快速排序方法
#define _CRT_NO_SECURE_WARNINGS
#include<stdio.h>
#include<iostream>
using namespace std;
int get_kBig(int* arr, int begin, int end, int k) {
if (begin < end) {
//随机取一个数为基准
int temp = arr[begin];
//i,j指针移动
int i = begin;
int j = end;
while (i < j) {
//从序列左边开始对比
//大于temp则j左移
while (i<j && arr[j]>temp) {
j--;
}
//找到小于temp值则将此值填在arr[i]
arr[i] = arr[j];
while (i < j && arr[i] <= temp) {
i++;
}
arr[j] = arr[i];
}
//将临时的值填在特定位置
arr[i] = temp;
//第k小值即为temp
if (i == k - 1) {
return arr[i];
}
//第k小值在temp的右边
else if (i > k - 1) {
return get_kBig(arr, begin, i, k);
}
//第k小值在temp左边且问题规模减小
else {
return get_kBig(arr, i + 1, end, k - i - 1);
}
}
}
int main() {
int arr[100] = { 0 };
int k = 0;
int i = 0;
int output;
cout << "请输入要求解的数组:";
for (i = 0;i < 100;i++) {
cin >> arr[i];
if (getchar() == '\n') {
break;
}
}
cout << "请输入求其第几大的数:";
cin >> k;
output = get_kBig(arr, 0, i, k);
cout << "此数为:" << output << endl;
system("pause");
return 0;
}
实验结果:
时间复杂度分析:
最坏复杂度是O()
平均复杂度是O(n)