版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Pecony/article/details/80065252
#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
const int N = 7;
int SecondSmall(int a[], int n);
void FindTwo(int a[], int i, int j, int &fmin1, int &fmin2);
int FindK(int a[], int n, int k);
void Swap(int &left, int &right);
int Select(int a[], int left, int right, int k);
int main ()
{
int a[N] = {2, 4, 1, 6, 5, 9, 0};
//cout << SecondSmall(a, N) << " is the second small number" << endl;
int k = 3;
cout << FindK(a, N, k) << " is the " << k << " small number" << endl;
}
//利用查找最小的两个数 才可以将原问题进行分解
int SecondSmall(int a[], int n)
{
int min1, min2;
FindTwo(a, 0, n - 1, min1, min2);
return min2;
}
void FindTwo(int a[], int i, int j, int &fmin1, int &fmin2)
{
int lmin1, lmin2, rmin1, rmin2;
int mid;
//递归出口
if(i == j)
fmin2 = fmin1 = a[i];
else if(i == j - 1)
{
fmin2 = a[i] > a[j] ? a[i] : a[j];//fmin2存储较大
fmin1 = a[i] > a[j] ? a[j] : a[i];
}
else
{
mid = (i + j) / 2;
FindTwo(a, i, mid, lmin1, lmin2);
FindTwo(a, mid + 1, j, rmin1, rmin2);
if(lmin1 < rmin1)
{
fmin1 = lmin1;
if(lmin1 != lmin2 && lmin2 < rmin1)//此处一定要加一个判断条件 以避免递归出口为i==j的情况min1和min2的值相同
fmin2 = lmin2;//Eg:2 3 1 则rmin1=rmin2=1 此时经过判读 则fmin1=fmin2=1 显然是错误的
//因此该判断取值是否相等
else
fmin2 = rmin1;
}
else
{
fmin1 = rmin1;
if(rmin1 != rmin2 && rmin2 < lmin1)
fmin2 = rmin2;
else
fmin2 = lmin1;
}
}
}
int FindK(int a[], int n, int k)//k代表要找第k小的数
{
if(k < 1 || k > n)
return 0;
return Select(a, 0, n - 1, k);//数组下标从0开始
}
int Select(int a[], int left, int right, int k)//在left-right之间选择第k小的数
{
int pivot, i, j;
if(left >= right)
return a[left];
pivot = a[left];//将最左边的元素作为枢轴
i = left + 1;//i是从左到右的指针 此时i指向枢轴元素的下一个元素
j = right;//j是从右到左的指针 初始时指向数组的最后一个元素
while(1)//将左边大于枢轴元素的元素与右侧小于枢轴元素的元素进行交换
{
while(i <= right && a[i] < pivot)
{
i++;
}
//若是不满足while条件 则i此时指向的是大于枢轴元素的元素
while(j >= left && a[j] > pivot)
{
j--;
}
if(i >= j)//此时left与right相遇
break;
Swap(a[i], a[j]);//a[i]作为低端元素大于枢轴元素 而a[j]作为高端元素小于枢轴元素 此时需要进行交换
}
//经过一次快速排序之后 此时j代表的是小于枢轴元素的最后一个元素的位置下标 因此j-left+1就是小于等于枢轴元素的个数
if(j - left + 1 == k)//j-left实质上就代表了一次快速排序之后左子段中的元素个数 若是恰好为k - 1个 则pivot即为所求
{
return pivot;
}
//此时证明小于枢轴元素的数少于所要求的k 因此需要继续进行递归调用
//将枢轴元素放到合适的位置 与快排相似 即枢轴元素之前的是小于它的 之后是大于它的
a[left] = a[j];
a[j] = pivot;
if(j - left + 1 < k)//此时证明所找出的元素数小于k个
return Select(a, j + 1, right, k - j - 1 + left);//k-(j-left+1) 已有j-left+1个元素是已知小于第k个元素的
else//找出的元素数大于k个 因此需要在小于枢轴元素的子序列中在进行查找
return Select(a, left, j - 1, k);
}
void Swap(int &left, int &right)//要进行引用传值 直接对数组元素进行交换
{
int t;
t = left;
left = right;
right = t;
}