快速排序
这次小编来分享一下自己对于快速排序的归纳与总结,之前小编分享了自己对堆排序、归并排序的看法,之所以最后写快速排序是因为小编认为快速排序可能在今后会使用的更多,而且小编认为快速排序的代码相比较前两种升级的排序算法更难理解
快速排序的特点
快速排序属于交换排序类,实际上是对之前冒泡排序的升级。实现方法就像我们上体育课时的整队型,体育老师往往会选择一个身高适中的同学站在中间,然后让其它同学根据身高的高低进行自主站位。但这样的说法比较笼统,实际上在左侧和右侧的站队中,每一侧的同学也分别选择了一位同学作为他们的衡量标准。
快速排序增大了记录的比较和移动距离,将关键字较大的记录从前头直接移到了后头,而关键字较小的记录则从后头直接移到了前头,达到了减少总的比较次数和移动次数的效果
快速排序的原理
快速排序的基本思想便是通过每一次执行,选择并确定一个关键字的位置,并使该关键字之前的关键字均小于它,之后的关键字均大于它。紧接着对这两部分进行同样的操作,直至整个序列有序
很多书上对第一个标杆值的选择有着不同的见解,弱小的小编还是比较喜欢取中间值作为标杆值
实现代码
#define swap(a,b) {typeof(a) t=a;a=b;b=t;}
void _quick_sort(int* arr,size_t left,size_t right)
{
if(left >= right) return;
//计算标杆的下标,pi → pivot index
int pi = (left+right)/2;
//备份标杆的值,借鉴了选择排序的思想,在找到最后的位置时才进行交换
int pv = arr[pi];
//备份左右下标
int l = left;
int r = right;
//左右下标相遇时结束
while(l < r)
{
//在标杆的左边寻找比它大的数
while(l<pi && arr[l] <= pv) l++;
if(l<pi)
{
arr[pi] = arr[l];
pi = l;
}
//在标杆的右边寻找比他小的数据
while(pi<r && arr[r] >= pv) r--;
if(pi<r)
{
arr[pi] = arr[r];
pi = r;
}
}
//还原标杆的值
arr[pi] = pv;
//递归的形式对左侧进行同样的排序方式
if(pi-left > 1) _quick_sort(arr,left,pi-1);
//递归的方式对右侧进行同样的排序方式
if(right-pi > 1) _quick_sort(arr,pi+1,right);
}
void quick_sort(int* arr,size_t len)
{
_quick_sort(arr,0,len-1);
}
实现原理图
以数组int arr[7]={50,20,40,80,30,70,10}
为例
第一轮:首先便是选择标杆值,小编喜欢直接取中间值作为标杆值(有兴趣的朋友可以去查一下三数取中与九数取中),并设置前后两个标志位来帮助我们记录过程,将标杆值临时存储的妙处在之后的执行中你便可以发现
while(l<r)
条件满足时,先执行while(l<pi && arr[l] <= pv)
,目的是为了在左侧找出比标杆值大的数,如若没有则将左标志位后移
很幸运的是第一次的循环过程中,左侧均是比标杆小的值
紧接着执行
while(pi<r && arr[r] >= pv);
,目的是为了在右侧找出比标杆值小的数,如若没有则将右标志左移
很不幸的是此时右侧的第一个数边比标杆值小,此时执行arr[pi] = arr[r]; pi = r;
注意此时arr[r]
上的值并没有随之改变,这也是为什么我们需要提前将标杆值存储下来,这里便是利用了选择排序的原理
判断外层循环
while(l < r)
,紧接着重复上述操作
跳出循环执行
arr[pi] = pv;
,得到第一趟排序的最终结果
小编将继续列出第二趟循环,此时执行
if(pi-left > 1) _quick_sort(arr,left,pi-1);
,将对第一趟排序后标杆值的左侧进行同样的排序
第一步仍是选出此时的标杆值
执行
while(l<pi && arr[l] <= pv)
第一次在左侧的寻找中我们便发现了比标杆值大的值,执行arr[pi] = arr[l]; pi = l;
执行
while(pi<r && arr[r] >= pv);
此时发现比标杆小的值
执行arr[pi] = arr[r]; pi = r;
判断
while(l<r)
后继续执行
执行while(l<pi && arr[l] <= pv)
此时在左侧找到比标杆值大的值
执行arr[pi] = arr[l]; pi = l;
执行
while(pi<r && arr[r] >= pv);
此时在右侧找到比标杆值小的值
执行arr[pi] = arr[r]; pi = r;
最后执行
这样便又完成了一次排序,接下来的步骤就由大家去探索啦
总结
小编认为快速排序相较而言更难理解,尤其是综合了选择排序的思想,将标杆值提前存放在另一变量中,在条件判断时并不是根据标杆值下标的位置进行比较,而是一直比较那个变量,保证了正确性,同时由于标杆值的位置会一直被别的数据取代而不会重复,直至得到最终位置再将数据放入,这样极大的减少了数据交换的次数,提高了效率