问题描述
Find the ith largest number of n numbers. (Use divide-and-conquer method)
翻译
找第k大数
思路
//* 1.首先将数组分为五五一组,并且进行排序
//* 2.将每个组a[2]也就是中位数,挪到a[0]-a[(r-p-4)/5],也就是数组前几个,(r-p-4)/5代表五个组的个数
//* 3.递归找到中位数的中位数
//* 4.对整个数组进行排序,使中位数的中位数左边<中位数的中位数,右边>中位数的中位数,参考快排
//* 5.定位中位数的中位数的索引,分情况递归。
代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string>
#include <algorithm>
#include <stdlib.h>
//思路
/*
* 1.首先将数组分为五五一组,并且进行排序
* 2.将每个组a[2]也就是中位数,挪到a[0]-a[(r-p-4)/5],也就是数组前几个,(r-p-4)/5代表五个组的个数
* 3.递归找到中位数的中位数
* 4.对整个数组进行排序,使中位数的中位数左边<中位数的中位数,右边>中位数的中位数,参考快排
* 5.定位中位数的中位数的索引,分情况递归。
*/
using namespace std;
int n;
const int N = 1e6 + 10;
int ans[N]; int k1 = 0;//mergeSort
void quickSort(int a[], int l, int r)
{
int i = l-1, j = r+1, mid = (l + r) / 2;
int tem = a[mid];
if (l >=r) return;
while (i < j)
{
do i++; while (a[i] > tem);
do j--; while (a[j] < tem);
if (i <=j)swap(a[i], a[j]);
}
quickSort(a, l, j);
quickSort(a, j + 1, r);
}
void mergeSort(int a[], int l, int r)
{
if (l >=r) return;
int mid = (l + r) / 2;
mergeSort(a, l, mid );
mergeSort(a, mid + 1, r);
int k1=0, i = l, j = mid+1;
while (i <=mid&& j<=r)
{
if (a[i] >=a[j])
ans[k1++] = a[i++];
else
ans[k1++] = a[j++];
}
while(i<=mid)
ans[k1++] = a[i++];
while (j <= r)
ans[k1++] = a[j++];
for (int i = l, j=0; i <=r; i++,j++)
{
a[i] = ans[j];
}
}
int partition(int a[], int l, int r,int x)//返回中位数索引,并且将数组排成小于中位数的中位数x
{
int i = l - 1, j = r + 1, tem = x;
while (i < j)
{
do i++;
while (a[i] > tem);
do j--;
while (a[j] < tem);
if (i < j)
swap(a[i], a[j]);
}
return i;
}//ith最大改为最小只需要将这个函数里面><以及快排的><反过来
int find(int a[], int l, int r, int k)
{
if (r - l < 75)
{
mergeSort(a,l,r);
return a[l+k - 1];//if n<75 O(n)=C
}
for (int i = 0; i < r; i+=5)
{
mergeSort(a, i, i + 4);//每五个一排序
}
for (int i = 0; i < (r - l - 4) / 5; i++)
{
swap(a[i+l], a[l + 5 * i+2]);//i表示第i组,将每组中位数换到数组前几位
//l+5*i+2和r-l-4搭配确保数组不越界
}
int x = find(a, l, (r - l - 4) / 5, (r - l - 4) / 10);//找出中位数的中位数,用x表示
int i = partition(a, l, r,x);//使i左边都<x,右边都>x;
int j = i-l+1;//左边一共j个数,k<j时从左边找,否则从右边找
if (k > j)
return find(a, i+1, r, k - j);
else
return find(a, l, i, k);
}
int main()
{
int k = 0;
cout << "Please enter the number of the array" << endl;
scanf("%d", &n);
int a[N];
cout << "Please enter which maximum number to look for" << endl;
cin >> k;
cout << "Please enter the array" << endl;
for (int i = 0; i < n; i++)
{
a[i]=rand();
cout << a[i] << " ";
}
cout << endl;
int mid=find(a, 0, n - 1,k);
cout << "The ith largest number is" << mid << endl;
return 0;
}
测试案例
运行结果
时间复杂度分析
速记
见思路 随时更新