关于快速排序的一些见解(递归和非递归以及优化)

最近看到一些笔试题,很多都考到了快速排序,故今天来此谈谈个人对快速排序的一些看法(理解可能有理解不恰到的地方,希望高人能多多指点)

快速排序,顾名思义,那就是一个字 快,但其实有的时候并不是我们所想的这样,当我们排序的数目很小时,快速排序可能还不如插入排序来得简单利落。

废话不多说,先来说说快速排序的核心思想

首先,它是利用找基准数来进行排序,最原始的快速排序是从最左边第一个数开始,放在了一个临时变量tmp中,然后左边下标为0这个位置就是一个空位,哎,就是左边有个坑。

然后呢,左边有坑,那么就从右边往左边走,当你找到一个右边的某个数比tmp中的数还小时,就拿右边的这个数去补左边的那个坑,那么坑就到右边那个位置了。

一样的,现在是右边有个坑,那么我们就要从左边开始往右走,当发现一个比tmp中数还要大的一个数字,那么就把这个数放到右边那个坑里头。

一直就这么走啊走,走啊走,左边挖个坑,右边来填坑,右边来挖坑,左边就来填坑,一直到一个临界条件,就是左边的数往右走,右边的数往左走,知道他俩相遇了,这时我们就找到了这一趟的一个基准数。然后就是返回这个low(下面来看看我们的图形界面)

这就是我们的第一步

这时我们的第二步,


这就是我们的第三步


这里只是告诉了大家快速排序中最重要的思想,接下来我们来说说如何去用我们得到的这个基准数

我们是这样来进行排序的,首先得到一个基准数,然后呢,基准数左边一组,右边一组,再在左边的数中找一个基准数,在右边也找一个基准数,然后一直这么分下去,直到它分得只剩一个数时,那么两边也就都是有序的了,当然,这些操作可以用递归来实现。

好了,光说这些思想不实现是没有任何意义的,所有说还是要拿来干货。、

int Parition(int *arr,int low,int high)
{
int tmp=arr[low];
while(low<high)
{
while(low<high &&arr[high]>=tmp)high--;
arr[low]=arr[high];
while(low<high &&arr[low]<=tmp)low++;
arr[high]=arr[low];
}
arr[low]=tmp;
return low;
}


void Qsort(int*arr,int low,int high)
{
if(low<high)
{
    int boundkey=Parition(arr,low,high);
int left=boundkey-1;
int right=boundkey+1;
Qsort(arr,low,left);
Qsort(arr,right,high);
}
}


void QuickSort(int*arr,int len)
{
Qsort(arr,0,len-1);
}




void Show(int *arr,int len)
{
for(int i=0;i<len;i++)
{
printf("%d ",arr[i]);
}
printf("\n");
}


int main()
{
int arr[] = { 1, -4, 2, 0, 66, 2, 5325, 1321, 90,321,54,658, 765};
int len = sizeof(arr) / sizeof(arr[0]);
QuickSort(arr,len);
Show(arr, len);
return 0;
}


这就是我们所说的快速排序,其实个人感觉挺简单的,然后我们来分析一下这个排序的一些特点

首先,快速排序的稳定性是不稳定的,我对稳定性的定义是看他是否(跳);感觉这种分析方法很独特,可能也有bug,

但对于快速排序还是有效的,因为它在找基准数的时候不停的在跳来跳去。我还要说的是时间复杂度


打字不好说明,就直接上图了,这3个复杂度分别是平均时间复杂度,和最好最坏情况下的时间复杂度。





好了好了,基本的都说完了,但是,对于快速排序还有几种优化,接下来我们来看看:


优化1:void Update_sort(int*arr,int low,int high)//优化1.0版本,为了防止已经有序的数列
{
int tmp;
tmp=arr[low];
arr[low]=arr[high];
arr[high]=tmp;
}


优化2:void Update_sort2(int*arr,int low,int mid,int high)//优化2.0 又称三分取中法,让基准数从最中间开始走
{
if(arr[mid]>arr[high])
{
Update_sort(arr,mid,high);
}
if(arr[mid]<arr[low])
{
Update_sort(arr,mid,low);
}
if(arr[low]>arr[high])
{
Update_sort(arr,low,high);
}
}

优化3:void Bubble(int *arr,int len)//优化3.0,此优化解决了数据少,直接用冒泡
{
int i,j,tmp=0;
for( i=0;i<len-1;i++)
{
for(j=0;j<len-1-i;j++)
{
if(arr[j]>arr[j+1])
{
tmp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=tmp;
}
}
}
}//当然也可以用插入啊什么的,这里就用下最简单的冒泡

优化4:void Update4_Gather(int *arr,int boundIndex,int low,int high,int *left,int*right)
{
if(low<high)
{
int count=boundIndex-1;
for(int i=boundIndex-1; i>=low;i--)
{
if(i!=count&&arr[i]==arr[boundIndex])
{
Update_sort(arr,count,i);
count--;
}
}
*left=count;


count=boundIndex+1;
for(int i=boundIndex+1;i<=high;i++)
{
if( i !=count&&arr[i]==arr[boundIndex])
{
Update_sort(arr,count,i);
count++;
}
}
*right=count;
}
}

这四种优化中,前面的3种比较简单,注释也写的很清楚,但由于优化4看起来代码比较长,所以着重来说一下这个优化4

优化4是解决了一种情况,那就是一组数中有大量相同的数,大家想想,数都一样的话,这些就没有比较的必要了,

这样可以提高我们效率,我们首先得到一个基准数,然后我们从这个基准数左右两边找,要是找到相同的数,那么就交换,等到找完所有的数后,相同的数就会都聚集在中间,那么,我们就把中间的数不进行排序,分别排两边的数。




前面说到都是用递归的方法,但好多笔试题都是让你写个非递归,哎呦,非递归?脑子一片空?很正常,我第一次接触这个题我也是两眼一抹黑,但大家最起码应该知道递归的定义,那就是在函数中再次调用函数本身,



void Sort_no(int* arr, int n)
       //快速排序的循环写法
{
int brr[50];
int count=0;
int low;
int high;
int mid;
brr[count++]=0;//这存放的数是数组最左边的数
brr[count++]=n-1;//这存放的数是数组最右的数
while(count>0)
{
high=brr[count-1];//重新定义high
low=brr[count-2];//重新定义low
if(low<high)
{
mid=Parition(arr,low,high);//还是一样先找一个中间的基准数
count-=2;
brr[count++]=mid+1;//这些都是为下次准备
brr[count++]=high;
brr[count++]=low;
brr[count++]=mid-1;//先将最左边的排序完
}
else count-=2;//再往回跳着排右边的
}
}//这就完成了非递归


说了这么多,能看到结尾的人我得先说声谢谢,毕竟能耐心看下来的人不多,中间或多或少还有一些理解不到位

或者解释不清的地方,大家若发现问题,请及时评论,一是我能及时发现问题,二也是不让更多人来犯这些问题。谢谢。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值