一种上课老师讲了没听懂的排序方法(
快排快排,可以说是相当的快了。具体实现的话弄懂也十分简单:
拿一个数组举例:
a [] = {5,7,4,6,3,1,2,9,8}
那么我们取第一个元素,将比它小的放在它的右边,将比他大的放在他的左边,即可得到:
那么怎么放有讲究嘛,这里我的写法是:
定义两个指针(两个数),一个指向数组开始,一个指向数组末尾
先将第一个元素也就是5拿出来:
那么这时第一个位置会产生一个空缺,拿谁来补,从后往前遍历,找到第一个比5小的数来补。
即数组末尾指针向前移动,找到比5小的数停止,并将该位置的数放到数组开头:
这时候右边又会产生一个空缺,拿谁补,从左边遍历,找到第一个比5大的数放在内个位置即可。
即开头的指针向后移动,找到比5大的数停止,并将该位置的数放到数组末尾指针的位置。
下一次又到从末尾指针开始找,找到后赋值给头指针,如此循环往复,知道两个指针碰头:
这时我们将之前提出的5放在碰头的位置:
ok,这一小步算是完成了。只要搞定这一步,剩下的就十分的简单了。
此时我们将数组分成了两个部分,即比五小和比五大,我们只需要利用递归的方式让左边的部分和右边的部分分别重复上述的过程即可。每一边结束的条件是只有一个元素或者无元素。
具体实现代码(菜鸡码风,轻喷:
#include<iostream>
using namespace std;
int p(int a[],int left,int right) //分左右两部的函数
{
int temp =a[left]; //将第一个元素取出,不一定为0,如果后面递归时就是最左
while(left<right)
{
while(left<right&&a[right]>=temp) //末尾指针向前找
right--;
a[left]=a[right];
while (left<right&&a[left]<=temp) //头指针向后找
left++;
a[right]=a[left];
}
a[left]=temp; //将之前的元素放入指针碰头位置
return left; //返回碰头位置下标
}
void quick_sort(int a[],int left, int right) //快排本体
{
int mid = p(a,left,right); //找到这部分的中间位置
if(left<right) //只要至少有两个元素就能排
{
quick_sort(a,mid+1,right);
quick_sort(a,left,mid-1);
}
}
int main()
{
int a[] = {5,7,4,6,3,1,2,9,8};
quick_sort(a,0,sizeof(a)/sizeof(int)-1);
for(int i = 0 ;i<sizeof(a)/sizeof(int);i++)
{
cout<<a[i];
}
}
成功得到输出:
说完了算法,下面来考察时间复杂度(平均):
用一种不严谨的算法来算十分简单:
如果一个数组有8个元素,那么我们找一个元素可以将它分成两部分——左边4个右边4个(强调不严谨算法)继续分:
除去一开始的一共分了3层即log8层这么多,那么每层运算都要左指针从左遍历,右指针从右遍历,将这个数组完全遍历一遍也就是进行了8次访问。
这样看来快排的复杂度也显而易见了为O(nlogn)
相较于冒泡的n方而言快了不少,但同样存在最坏情况,如果我们采用上述办法来对一个逆序数组进行顺序排序的话,时间复杂度一样会来到O(n方),各位同学们可以自行模拟一下。
解决办法十分简单,即开始时在数组中随机选择一个数,让它与第一个数进行换位,继续上述方法即可,这样来做就无法刻意构建出一个最坏情况,从而大大提高此算法的稳定性。