思路:
(1)利用类快排的算法来写
(2)使用k大小的最小堆实现,根节点处就是所求值
实现想法:
使用连续数组来保存最小堆,并记录数组中节点个数
从根节点依次插入序列中的数据:
当节点个数==k时,如果小于根节点则直接跳过,否则删除根节点,将新的插入
测试数据(只有一组):
2
10
2
95 52 14 71 30 28 71 29 73 88
7
10
2
5 29 70 25 6 40 9 47 71 12
4
代码实现:
int* a; //用于保存输入的序列
int result = -1; //用于保存类快排的计算结果。
int* mindui;//保存最小堆
int c = 0; //用来计数最小堆中已有节点数
void swapp(int x, int y) { //交换
int t = a[x];
a[x] = a[y];
a[y] = t;
}
void add(int x) { //最小堆插入:从尾部开始比较
int i = c, j = 0;
while (true)
{
if (i % 2 == 0) {
j = i/ 2; //得到父节点的索引
}
else {
j = (i - 1) / 2;
}
if (mindui[j]<x||i==0) { //注意此处回溯到根节点的限制,否则会出错
mindui[i] = x;
break;
}
else {
mindui[i] = mindui[j];
i = j;
}
}
}
void delete_head(int x) { //调整堆,并把根节点删去
int j = 0, i = 0;;
while(true) {
j = (i + 1) * 2 - 1;//左孩子节点
if (j >= c) {//即i是叶节点时,停止
mindui[i] = x;
break;
}
if (j+1<c) {//对数组进行访问的索引都要检查是否越界
if (mindui[j] > mindui[j + 1]) { //因为是最小堆
j++;
}
}
if (mindui[j] > x) {
mindui[i] = x;
break;
}
else {
mindui[i] = mindui[j];
i = j;
}
}
}
void q_find_k(int b, int e, int k) { //使用类快排实现
if (b >= e) {
return;
}
int temp = a[b];//记录基准
int tempb = b;
int tempe = e;
while (true) {
while (e >b && a[e] >= temp) {
e--;
}
while (b <= e && a[b] <= temp) {
b++;
}
if (b >= e) {
if (e==k-1) {//找到了第k大的,因为e是数组的索引,所以要减1
result = temp;
return;
}
else {
swapp(tempb, e);
q_find_k(tempb, e - 1, k);
q_find_k(e + 1, tempe, k);
break;
}
}
else {
swapp(b, e);
}
}
}
void d_find_k(int n, int k) { //最小堆找第k大的
mindui[0] = a[0];
c++;
for (int i = 1; i < n; i++) {
if (c == k) { //相当于最小堆的删除操作
if (a[i] <= mindui[0]) {
continue;
}
delete_head(a[i]);
}
else { //相当于最小堆的插入操作
add(a[i]);
c++;
}
}
}
int main() {
int m,n,k,q;
cout << "此程序实现寻找长度为n的序列中第k大的数并输出。请输入测试次数:";
cin >> m;
for (int i = 0; i < m; i++) {
cout << "请输入n:";
cin >> n;
a = new int[n];
cout << "随机生成序列输入1,自行输入输入2:";
cin >> q;
if (q == 1) {
cout << "随机生成长度为n的随机序列:";
srand(time(0));
for (int j = 0; j < n; j++) {
a[j] = rand() % 100;
cout << a[j] << " ";
}
cout << endl;
}
else {
cout << "请输入序列:";
for (int j = 0; j < n; j++) {
cin >> a[j];
}
}
while (q != -1) {
cout << "使用类快排计算输入1,使用最小堆计算输入2,停止此次测试输入-1:";
cin >> q;
if (q == 1) {
cout << "请输入k:";
cin >> k;
q_find_k(0, n - 1, n - k); //修改此处n-k为k即可改为寻找第k小的数
if (result == -1) { //表示第k大的数本来就在相应的位置
result = a[n - k];
}
cout << "类快排的计算结果为:" << result << endl;
result = -1;//因为多次测试所以需要将全局清零
}
else if (q == 2) {//最小堆计算:
cout << "请输入k:";
cin >> k;
mindui = new int[k];
mindui[0] = -1;
d_find_k(n, k);
cout << "k大小的最小堆的计算结果为:" << mindui[0] << endl;
}
}
}
return 0;
}