一、 实践题目名称----找第k小数 

 

二、 问题描述----在n个无序的整数中找出第k小的数,要求平均时间复杂度为O(n)

 具体题目如图:

 

三、 算法描述

void FindK (int *a,int left,int right,int k)
{//找第k小
    int mid=定位基准位置(调用paritition)

    if(mid==第k小,即下标为k-1)
    {
       cout<<a[mid];//直接输出
    }
    else if(mid>k-1)
    {//第k小的数比mid小,则在mid左段查找
       find(a,left,mid-1,k);
    }
    else
    {//第k小的数比mid大,则在mid右段查找
       find(a,mid+1,right,k);
    }
}

int paritition(int *a,int left,int right)
{//定位基准下标——使得基准左边的数全小于基准,右边的数全大于基准 

   int i = left,j = right+1;
   int x = a[left];
 
   while(1)
   {
      while(a[++i] < x&&i < right);//从左向右找到大于基准的数 
      while(a[--j] > x);//从右向左找小于基准的数 
      if(i >= j)
      {//如果i>j,说明所有的数已遍历完,为则退出 
        break;//此时i下标为大于基准的数,j下标为小于基准的数 
      }
      swap(a[i],a[j]);//交换找到的两个数的位置使得基准左边的数 
   }
   
   //将基准放在其固定位置 
   a[left] = a[j]; 
   a[j] = x;
   
   return j;//返回下标位置
   
}

以下为具体代码实现

#include<iostream>
using namespace std;

int paritition(int *a,int left,int right)
{//定位基准下标

   int i = left,j = right+1;
   int x = a[left];
 
   while(1)
   {
      while(a[++i] < x&&i < right);//左侧
      while(a[--j] > x);//右侧
      if(i >= j)
      {
        break;
      }
      swap(a[i],a[j]);
   }
   
   a[left] = a[j];
   a[j] = x;
   
   return j;
   
}

void find(int *a,int left,int right,int k)
{//找第k小
    int mid=paritition(a,left,right);
    
    if(mid==k-1)
    {
       cout<<a[mid];//直接输出
    }
    else if(mid>k-1)
    {
       find(a,left,mid-1,k);//找左边
    }
    else{
       find(a,mid+1,right,k);//找右边
    }
    
}

int main()
{
    int n,k;
    cin>>n>>k;
    int a[10000];
    
    for(int i=0;i<n;i++)
    {
       cin>>a[i];
    } 
     
    find(a,0,n-1,k);
 
    return 0;
}
找第k小的数

 

四、 算法时间及空间复杂度分析

1. 时间复杂度:paritition函数需要遍历整个序列,时间复杂度为O(n), 而FindK函数利用二分思想,且只需要处理一半(最坏情况处理规模为n-1),时间为T(n/2),所以总的T(n) = O(n) + T(n/2) = O(n) + O(nlog21) = O(n) + O(1) = O(n)

2. 空间复杂度:递归算法的空间复杂度 S(n) = 递归深度*每次递归所需辅助空间k 

在本题中,递归需要开辟辅助空间,递归深度为log2n, 每次递归所需辅助空间为1,所以递归过程空间复杂度为O(log n),所以总的S(n) = O(1) + O(log n) = O(log n);

 

五、心得体会

1. 快排能够在每一次确定基准在最终排序序列的确定位置下标,因此找第k小的数能够利用确定基准的位置来比较判断第k小的数应该在哪个范围,从而提高查找的效率。

2. 在此次实践中,需要注意的是:

(1)定位基准的函数里面,利用循环查找大于或小于基准的数时要注意while循环进入的条件,比如从左向右找大于基准的数,循环里应该是小于,则大于时才退出循环得到我们要的数,符号不能乱。

(2)边界问题

 

 posted on 2020-10-04 09:48  刘熳如  阅读(116)  评论(0编辑  收藏  举报