快排的三种算法与实现

交换排序之快排

快排是一种时间复杂度为O(N*longN)的不稳定排序算法,其最差情况下算法的时间复杂度为O(N^2)。

一. 快排实现思想的基本框架

思想:
在队列中选取key,作为划分标准,将其划分为俩段,然后通过交换使得序列的左边的数小于key,右边的数大于key。
通过递归对序列不断划分,直至序列中只有一个元素时停止,通过对其子序列的数采用交换的手段达到排序的目的。
实现:

void QuickSort(int* arr,int begin,int end)
{
  if(begin>=end)
    return;
    //将数组划分为([begin,keyindex-1] keyindex [keyindex+1,end])三段
   int keyindex=PartSort1(arr,begin,end);
   //int keyindex=PartSort2(arr,begin,end);
   //int keyindex=PartSort3(arr,begin,end);

  //走俩边
  QuickSort(arr,begin,keyindex-1)//前面的一段
  QuickSort(arr,keyindex+1,end);   //后边的一段
}

二. 快排的三种对子序列排序的实现思路

1.左右指针法实现

算法思想:
在队列中任选一个数key,将序列画分为俩段,通过定义序列头部和尾部俩个指针,进行交换,将序列的左边换为小于key的数,右边换为大于key的数。
具体代码实现

void Swap(int* p1,int*p2)
{
  int temp=*p1;
  *p1=*p2;
  *p2=temp;
}

 int PartSort1(int *arr,int begin,int end)//所传参数为下标
{
   int key=arr[end];//key为划分的标准
   int index=end;
   while(begin<end)
   {
     //从左向右找比key大的数,并且记录其下标
     while(begin<end && arr[begin]<=key)
       ++begin;
     //从右向左找比key小的数,并且记录其下标
     while(begin<end && arr[end]>=key)
       --end;
     //对找到的数进行交换
     Swap(&arr[begin],&arr[end]);
   }
   //待循环完毕之后将最后一个大于key的数与key交换
  Swap(&arr[begin],&arr[index]);
retrun begin;
}

2.挖坑法

算法思想:
在队列中选取一个key做为坑的位置并且保存其值(一般选取序列最后一个或者第一个值),并以key为划分标准来将序列划分为俩段,使得key前面的值都小于等于key,后边的值都大于等于key。
具体实现:

int PartSort2(int* arr,int begin,int end)
{
  int key=arr[end];//end为第一个坑的位置
  while(begin<end)
  {
    //找大于key的数
     while(begin<end && arr(begin)<=key)
      ++begin;
      //找到之后,将其放至挖好的坑中,同时end的位置形成新的坑位
      arr[end]=arr[begin];
      //找小于key的数
      while(begin<end && arr[end]>=key)
       --end;
       //与上同理
      arr[begin]=arr[end]
  }
  //用开始保存的key填补最后一个坑位
  arr[begin]=key;
return begin;
}

3.前后指针法

算法思想:
在序列中选取一个key,将序列画分为俩段,定义一前一后俩个指针,让前一个指针找小于key的值,找到之后与后一个指针对应的值进行交换(把大的值换到后边),以此将序列达到key前面的值都小于等于key,后边的值都大于等于key的目的。
具体实现:

int PartSort(int* arr,int begin,int end)
{
  int key=arr[end];
  int cur=begin;
  int prev=begin-1;
  while(cur<end)
  {
  //找到小于key的值,并将其换到前边//让prev向前走
    if(arr[cur]<key &&++prev!=cur)
      Swap(&arr[cur],&arr[prev]);
    ++cur;
  }
//循环走完后,最后一步交换之后prev停留位置的值(arr[prev])是要比key小的,
//因此需要向前再走一步,走到大于key的值的位置,然后再将将大的换到后边去
  ++prev;
  Swap(&arr[prrev],&arr[end]);
return prev;
}

三. 对快排的性能优化

原因:
假设数组已然有序,那么根据前面的算法,依旧要对数组进行划分,直至不可划分为止,然后在对其进行排序,而且每次排序时仍然要一步一步走下去。那么我们是否可以在key这个值的选取上做点文章呢,且看下边代码
三数取中法:

//让key的值直接取数组中间的值
//这样就可以达到提高效率的目的
int GetMidIndex(int* arr,int begin,int end)
{
  int mid=(begin+end)>>1;
   if (arr[begin] < arr[mid])
 {
  if (arr[mid] < arr[end])
   return mid;
  else if (arr[begin] < arr[end])
   return end;
  else
   return begin;
 }
 else
 {
  if (arr[mid] < arr[end])
   return mid;
  else if (arr[begin] < arr[end])
   return begin;
  else
   return end;
 }
}

在以上的三种对子序列进行排序的算法中只需加上以下俩句代码,便可达到优化的目的。

int midindex=GetMidIndex(arr,begin,end);
Swap(&arr[midindex],&arr[end]);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值