寻找第k小元素

 

//本算法时间复杂度 O(n)
#include<stdio.h>
//#include<conio.h>//getch()头文件
#include<math.h>
#include<conio.h>
#include<iostream>
using namespace std;
//选取n个中最大的k个元素,用”五分化中项的中项“(快速排序的划分方法)方法,先找第k大的元素
/* The SELECT algorithm determines theith smallest of an input array ofn > 1 elements by executing the following steps. (Ifn = 1, then SELECT merely returns its only input value as theith smallest.)(算法SELECT通过执行下列步骤来确定一个有n>1个元素的输入数组中的第i小的元素。(如果n=1,则SELECT返回它的唯一输入数值作为第i个最小值。))
1. Divide then elements of the input array into? groups of 5 elements each and at most one group made up of the remainingn mod 5 elements.
2. Find the median of each of the? groups by first insertion sorting the elements of each group (of which there are at most 5) and then picking the median from the sorted list of group elements.
3. Use SELECT recursively to find the medianx of the? medians found in step 2. (If there are an even number of medians, then by our convention,x is the lower median.)
4. Partition the input array around the median-of-mediansx using the modified version of PARTITION. Letk be one more than the number of elements on the low side of the partition, so thatx is thekth smallest element and there aren-k elements on the high side of the partition.(利用修改过的partition过程,按中位数的中位数x对输入数组进行划分,让k比划低去的元素数目多1,所以,x是第k小的元素,并且有n-k个元素在划分的高区)
5. Ifi =k, then returnx. Otherwise, use SELECT recursively to find theith smallest element on the low side ifi <k, or the (i -k)th smallest element on the high side ifi >k.(如果要找的第i小的元素等于程序返回的k,即i=k,则返回x。否则,如果i<k,则在低区递归调用SELECT以找出第i小的元素,如果i>k,则在高区间找第(i-k)个最小元素)
*/
#define N 58
int array[N];
//int median[N/5+1];
void swap(int &s1,int &s2)
{
 int temp;
 temp=s2;s2=s1;s1=temp;  
}
int find_median5(int *arr,int i,int n)//i表示起始位置
{
    int arr_s[5];//用arr_s使arr保持不变
  for(int k=i,j=0;j<n;k++,j++)
   arr_s[j]=arr[k];
 //cout<<"在 5 中位数选择"<<endl;
  i=0;
 switch(n)
 {
  case 1:
      return arr_s[i];
  break;
  case 2:
       return arr_s[i+1];//两个元素返回后边的那个
  break;
  case 3:
       if(arr_s[i]<arr_s[i+1])
        if(arr_s[i+1]<arr_s[i+2])return arr_s[i+1];
        else if(arr_s[i]<arr_s[i+2])return arr_s[i+2];
             else return arr_s[i];
       else
         if(arr_s[i+1]>arr_s[i+2])return arr_s[i+1];
         else if(arr_s[i]>arr_s[i+2])return arr_s[i+2];
             else return arr_s[i];
  break;
  case 4://四个相当于从前三个元素选中间元素
       if(arr_s[i]<arr_s[i+1])
        if(arr_s[i+1]<arr_s[i+2])return arr_s[i+1];
        else if(arr_s[i]<arr_s[i+2])return arr_s[i+2];
             else return arr_s[i];
       else
         if(arr_s[i+1]>arr_s[i+2])return arr_s[i+1];
         else if(arr_s[i]>arr_s[i+2])return arr_s[i+2];
             else return arr_s[i];
  break;
  case 5:
       if(arr_s[i]>arr_s[i+1])swap(arr_s[i+1],arr_s[i]);//{temp=;arr[i+1]=;arr[i]=temp;}//arr[i+1]大数
        if(arr_s[i+2]>arr_s[i+3])swap(arr_s[i+3],arr_s[i+2]);//{temp=;arr[i+3]=;arr[i+2]=temp;}//arr[i+3]大数
        if(arr_s[i+1]>arr_s[i+3])
        {
            //cout<<arr[i+1]<<"  before swap  "<<arr[i+3]<<endl;
            swap(arr_s[i+1],arr_s[i+3]);//保证i+3是大元素
            //cout<<arr[i+1]<<"  after swap  "<<arr[i+3]<<endl;
             swap(arr_s[i],arr_s[i+2]);
        }//保证i+3大于i+2,i+1大于i //arr[i+3]可以排除不是中间元素,i小于i+1,i+2,i+4中选中间元
         if(arr_s[i+2]<arr_s[i+4])//arr[i+4]同样可排除(都是比中间元素大),i也可以排除(因为剩下的三个元素中要找最大的,而i+1大于i),
           if(arr_s[i+1]>arr_s[i+2])return arr_s[i+1];
              else return arr_s[i+2];
            else//arr[i+2]同样可排除,还有i
              if(arr_s[i+1]>arr_s[i+4])return arr_s[i+1];
              else return arr_s[i+4];        
  break;
 }
  //default:
    //      ; 
}
//int find_median(int *arr,int low,int high,int n)
int find_median(int *arr,int low,int n)
{  
   if(n<=5) return find_median5(arr,low,n);
   //if(n<=5) return find_median5(arr,low,high-low+1);
  else {
       //cout<<"in median n="<<n<<endl;
        int *median=new int[n/5+1];//暂存中位数信息
      int j,k,m=5;
       if(n%5!=0)//除不开,最后一组参与中位数选择
       { //cout<<"除不开,最后一组参与中位数选择"<<endl;
          for(int i=0,j=low;i<n/5+1;i++)//最后一组参与中位数选择
      //for(int i=0,j=low;i<(high-low+1)/5+1;i++)
     {
          if(i==n/5)m=n%5;//最后一组元素个数
    median[i]=find_median5(arr,j,m);//组内球中位数 ,返回五个数中位数,存到median【】
    //cout<<median[i]<<" median5[i] "<<endl;//i表示当前五个元素起始位置,m表示当前数组中元素个数。
    j+=5;
      }
      //return find_median(median,0,n/5,n/5+1);//从median中选中位数
         return find_median(median,0,n/5+1);
    }
       else
       {
           //cout<<"chu开,最后一组参与中位数选择"<<endl;
          for(int i=0,j=low;i<n/5;i++)
      {
    median[i]=find_median5(arr,j,5);//组内球中位数 ,返回五个数中位数的位置,
    //i表示当前五个元素起始位置,m表示当前数组中元素个数。
    j+=5;
      }
          //return find_median(median,0,n/5-1,n/5);
    return find_median(median,0,n/5);
       }     
    }
}

int find_k_th(int *arr,int low,int high,int k,int n) //找第k小的,如果找大的,下面while变化
{int mid_value,temp,piv,i,j,flag;
if(n==1)return arr[low];//;||low==high
else {
        i=low;j=high;temp=arr[low];
        //mid_value=find_median(arr,low,high,n);
  mid_value=find_median(arr,low,n);//注意查找中位数过程中不能改变数组的值和顺序
  //cout<<" current value is "<<mid_value<<endl;
  if(arr[low]==mid_value) flag=low;
        while(i<j)
        {
   while(arr[j]>=mid_value&&i<j){if(arr[j]==mid_value)flag=j;//标记没移动时的位置
    j--;}//相等的放到后面
  if(i<j){
   arr[i]=arr[j];i++;}
        while(arr[i]<mid_value&&i<j)i++;//相等的不放到前面
  if(i<j){
   if(arr[i]==mid_value)flag=j;//标记mid_value移动后的位置
   arr[j]=arr[i];j--;}  
       
        }
  arr[i]=temp;
  if(arr[i]==mid_value)flag=i;
  /*cout<<" out of while "<<endl;
  for(piv=low;piv<=high;piv++)
   cout<<arr[piv]<<" ";
  cout<<endl;
  cout<<" mid_value  "<<mid_value<<"  position is  "<<flag+1<<endl;
        */
        if(arr[i]<mid_value)//相等的放到后面
        { //和i后面的交换,保证i+1前面都小于midvalue,后面大于等于
   if(i+1!=flag)swap(arr[i+1],arr[flag]);
         if(k-1==i-low+1)return mid_value;//前面有i-low+1个,k应是第i-low+2个
   else if (k-1<i-low+1)return find_k_th(arr,low,i,k,i-low+1);//中间值放在左边,左边元素多一个
              else return find_k_th(arr,i+2,high,k-i-2+low,high-i-1);
   /*if(k<=i+1)return find_k_th(arr,low,i,k,i-low+1);//在左边
         else return find_k_th(arr,i+1,high,k-i-1,high-i);*/
        }
        else
        {//if(i!=low)
  // {
   if(i!=flag)swap(arr[i],arr[flag]);
    if(k-1==i-low)return mid_value;
     else if(k-1<i-low)return find_k_th(arr,low,i-1,k,i-low);
          else return find_k_th(arr,i+1,high,k-i+low-1,high-i);
  // }
   //else
   //{
  // if(k==1)return mid_value;
  // else return find_k_th(arr,low,high,k,high-low+1);
   //}
        }
  //for(int ij=low;ij<=high;ij++)
  // cout<<arr[ij]<<" ";
  //cout<<endl;
    }
}

void Insertion_sort(int *array,int n)//①插入排序
{
 int tmp=0;
 int i,j=0;
 for(i=1;i<n;i++)
 {
  j=i;
  tmp=array[i];
  while(j>0&&array[j-1]>tmp)
  {
   array[j]=array[j-1];
   j--;
  }
  array[j]=tmp;
 }
}

int main()
{
    int array[N];
    for(int i=0;i<N;i++)
    array[i]=rand()%1000;
    for(i=0;i<N;i++)
    cout<<array[i]<<" ";
    cout<<endl;
    //cout<<"中间元素 "<<find_median(array,0,N-1,N-0)<<endl;
    cout<<"中间元素 "<<find_median(array,0,N-0)<<endl;
 for(i=1;i<=N;i++)
 //i=3;
 cout<<"the "<<i<<" th small is  "<<find_k_th(array,0,N-1,i,N-0)<<endl;
    Insertion_sort(array,N);
    for(i=0;i<N;i++)
    cout<<array[i]<<" ";
    cout<<endl;
    //cout<<endl<<find_median(array,0,N-1,N-0)<<endl;
    //cout<<"the k th small is  "<<find_k_th(array,0,N-1,6,N)<<endl;
    //getch();
 return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值