原本想看看动态规划的,瞄到了一个线性时间选择的问题,之前看懂了又忘了,于是加深加深印象。
问题:
给定线性序集中n个元素和一个整数k,1<=k<=n,要求找出这n个元素中第k小的元素。
将元素每五个分为一组,分别找出中位数,再找到中位数的中位数。基于这个数进行快排。如此一来,每次都至少能分出1/4左右的元素,避免直接快排产生的最坏情况。 让人不禁感叹这就是大师的智慧。😲
代码:
#include<iostream>
#include<cstdio>
using namespace std;
void Swap(int &a, int &b)
{
int temp;
temp = a;
a = b;
b = temp;
}
void Quicksort(int nums[], int left, int right)//快排
{
if(left > right)return ;
int low = left;
int high = right;
int key = nums[low];
while (low < high)
{
while(low < high && nums[high] >= key) high--;
nums[low] = nums[high];
while(low < high && nums[low] < key) low++;
nums[high] = nums[low];
}
nums[low] = key;
Quicksort(nums,left,low - 1);
Quicksort(nums,low + 1,right);
}
int Partition(int nums[], int p, int r, int x)//数组从p到r,基于x快排 !一次 !
{
if(r < p)return -1;
int i;
for( i = p; i <= r; i++){
if(nums[i] == x)break; //找到了x的i
}
Swap(nums[i],nums[p]);
int low = p;
int high = r;
int key = nums[low];
while (low < high)
{
while(low < high && nums[high] >= key) high--;
nums[low] = nums[high];
while(low < high && nums[low] < key) low++;
nums[high] = nums[low];
}
nums[low] = key;
return low;
}
int Select(int nums[], int p, int r,int k)//数组从p到r,找到第k个
{
if(r - p < 75)
{
Quicksort(nums,p,r);
return nums[p+k-1];
}
for(int i = 0;i < (r-p-4)/5; i++)//分成5组
{
Quicksort(nums,p+(i*5),p+(i*5)+4);//每组分别快排
Swap(nums[p+(i*5)+2],nums[p+i]);//集合各组的中位数
}
Quicksort(nums,p,p -1 + (r-p-4)/5);
int x = nums[p + (r-p-4)/10];
int i = Partition(nums,p,r,x); //i是x的位置 ,x已被快排1次
int j = i - p + 1;//j是x的排名
//如果k在x的右边 ,x前去掉
if(k <= j) Select(nums,p,i,k);
else Select(nums,i+1,r,k-j);
}
int main()
{
int x;
//数组a存了0-79
int a[80]= {3,1,7,6,5,9,8,2,0,4,13,11,17,16,15,19,18,12,10,14,23,21,27,26,25,29,28,22,20,24,33,31,37,36,35,39,38,32,30,34,43,41,47,46,45,49,48,42,40,44,53,51,57,56,55,59,58,52,50,54,63,61,67,66,65,69,68,62,60,64,73,71,77,76,75,79,78,72,70,74,
};
cin>>x;
printf("第%d小的数是%d\n",x,Select(a,0,79,x));
return 0;
}
看这俩哥的:
https://blog.csdn.net/vandance/article/details/82879085
https://blog.csdn.net/m0_37579232/article/details/80178000