#include <bits/stdc++.h>
using namespace std;
int divide=0;
int Partition(int a[],int p,int r,int val)
{
int pos;
for(int q=p; q<=r; q++)
{
if(a[q]==val)
{
pos=q;
break;
}
}
swap(a[p],a[pos]);
int i=p,j=r+1,x=a[p];
while(1)
{
while(a[++i]<x&&i<r);
while(a[--j]>x);
if(i>=j)break;
swap(a[i],a[j]);
}
a[p]=a[j];
a[j]=x;
cout<<"输出partition函数的值"<<endl;
for(int i=p;i<=r;i++){
cout<<a[i]<<" ";
}
cout<<endl;
return j;
}
int Select(int a[],int p,int r,int k,int id,int level)
{
cout<<"访问id为"<<id<<"的结点"<<endl;
if(r-p+1<=5)
{
sort(a+p,a+r+1);
return a[p+k-1];
}
//找中位数的中位数,r-p-4即上面所说的n-5
for(int i=0; i<=(r-p-4)/5; i++) //把每个组的中位数交换到区间[p,p+(r-p-4)/4]
{
int s=p+5*i;
sort(a+s,a+s+5);
swap(a[p+i],a[s+2]);//交换每组中的中位数到前面
}
//(r-p-4)/5表示组数-1,则[p,p+(r-p-4)/5]的区间长度等于组数
//cout<<"进入第"<<level<<"层"<<id<<"的左子树"<<endl;
int x=Select(a,p,p+(r-p-4)/5,(r-p+6)/10,2*id,level+1);//求中位数的中位数
//(r-p+6)/10 = (p+(r+p-4)/5-p+1+1)/2
int i=Partition(a,p,r,x),j=i-p+1;
cout<<"第"<<divide++<<"次对大小为"<<r-p+1<<"的数组进行划分,基准是"<<x<<"起点是"<<p<<"终点为"<<r<<endl;
//cout<<p<<" "<<r<<" "<<x<<endl;
//if(k!=j)cout<<"k:"<<k<<"j:"<<j<<"进入第"<<level<<"层"<<id<<"的右子树"<<endl;
if(k==j)return a[i];
else if(k<j)return Select(a,p,i-1,k,2*id+1,level+1);
else if(k>j)return Select(a,i+1,r,k-j,2*id+1,level+1);
}
int main()
{
int x;
//数组a存了0-79
/* int a[80]= {47,46,45,49,48,42,40,44,53,51,57,56,55,59,58,
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,
52,50,54,63,61,67,66,65,69,76,75,79,78,72,70,74,68,62,60,64,73,71,77,
};
*/
int a[]={29,22,28,14,45,10,44,23,9,39,38,52,6,5,50,37,11,26,3,15,2,53,40,54,25
,42,12,19,30,16,18,13,1,48,41,24,43,46,47,17,34,20,31,32,33,35,04,49,51,07,36,27,8,21};
while(cin>>x){
divide=0;
int n=sizeof(a)/sizeof(a[0]);
printf("第%d大的数是%d\n",x,Select(a,0,n-1,x,1,1));
}
}
还有一个老外版本的:https://www.geeksforgeeks.org/kth-smallestlargest-element-unsorted-array-set-3-worst-case-linear-time/
// C++ implementation of worst case linear time algorithm
// to find k'th smallest element
#include<iostream>
#include<algorithm>
#include<climits>
using namespace std;
int partition(int arr[], int l, int r, int k);
// A simple function to find median of arr[]. This is called
// only for an array of size 5 in this program.
int findMedian(int arr[], int n)
{
sort(arr, arr+n); // Sort the array
return arr[n/2]; // Return middle element
}
// Returns k'th smallest element in arr[l..r] in worst case
// linear time. ASSUMPTION: ALL ELEMENTS IN ARR[] ARE DISTINCT
int kthSmallest(int arr[], int l, int r, int k,int id,int level)
{
cout<<"访问id为"<<id<<"的结点"<<endl;
// If k is smaller than number of elements in array
if (k > 0 && k <= r - l + 1)
{
int n = r-l+1; // Number of elements in arr[l..r]
// Divide arr[] in groups of size 5, calculate median
// of every group and store it in median[] array.
int i, median[(n+4)/5]; // There will be floor((n+4)/5) groups;
for (i=0; i<n/5; i++)
median[i] = findMedian(arr+l+i*5, 5);
if (i*5 < n) //For last group with less than 5 elements
{
median[i] = findMedian(arr+l+i*5, n%5);
i++;
}
// Find median of all medians using recursive call.
// If median[] has only one element, then no need
// of recursive call
cout<<"进入第"<<level<<"层"<<id<<"的左子树"<<endl;
int medOfMed = (i == 1)? median[i-1]:
kthSmallest(median, 0, i-1, i/2,id*2,level+1);
// Partition the array around a random element and
// get position of pivot element in sorted array
int pos = partition(arr, l, r, medOfMed);
cout<<"l:"<<l<<"r:"<<r<<"轴:"<<medOfMed <<endl;
// If position is same as k
if (pos-l == k-1)
return arr[pos];
cout<<"进入第"<<level<<"层"<<id<<"的右子树"<<endl;
if (pos-l > k-1) // If position is more, recur for left
return kthSmallest(arr, l, pos-1, k,id*2+1,level+1);
// Else recur for right subarray
return kthSmallest(arr, pos+1, r, k-pos+l-1,id*2+1,level+1);
}
// If k is more than number of elements in array
return INT_MAX;
}
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
// It searches for x in arr[l..r], and partitions the array
// around x.
int partition(int arr[], int l, int r, int x)
{
// Search for x in arr[l..r] and move it to end
int i;
for (i=l; i<r; i++)
if (arr[i] == x)
break;
swap(&arr[i], &arr[r]);
// Standard partition algorithm
i = l;
for (int j = l; j <= r - 1; j++)
{
if (arr[j] <= x)
{
swap(&arr[i], &arr[j]);
i++;
}
}
swap(&arr[i], &arr[r]);
return i;
}
// Driver program to test above methods
int main()
{
int arr[] = {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,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,37,36,35,39,38,32,30,34,43,41,47,
46,45,49,48,42,40,44,53
};
int n = sizeof(arr)/sizeof(arr[0]), k = 3;
while(cin>>k){
cout << "K'th smallest element is "
<< kthSmallest(arr, 0, n-1, k,1,1)<<endl;
}
return 0;
}