线性时间选择教材程序订正版

这篇博客介绍了如何在最坏情况下以线性时间复杂度找到未排序数组中的第k小元素。算法通过分治策略,将数组划分为小块并找到中位数的中位数作为基准值,然后进行划分,递归地在左右子数组中继续寻找,直到找到目标元素。
摘要由CSDN通过智能技术生成
#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;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值