七大排序算法之快速排序

来源:互联网 发布:农村淘宝开业计划书 编辑:程序博客网 时间:2024/06/06 16:34

七大排序算法之快速排序

  • 七大排序算法之快速排序
    • 快速排序的介绍
    • 快速排序的核心思想
    • 简单例子
    • 时间复杂度的讨论
    • 每轮快排的具体实现
    • 代码

快速排序的介绍:

快速排序是C. A. R. Hoare在1962年提出的,平均时间复杂度为O(nlogn)级的高效率算法,也是目前O(nlogn)的排序算法中使用最多的算法,这段时间想把自己学过的算法都整理一遍,于是就写了这篇博客,来记录自己的学习过程吧…也希望能给初学者带来一些帮助。


快速排序的核心思想

每次排序都需要以序列的第一个值为关键值,将序列分成两个部分,并且使其中一部分的值都大于关键值,而另一部分的值都小于关键值,然后再分别对两个部分都再次进行本操作,直至某次操作的部分只有一个值。整个排序过程可以通过递归的方式进行,使整个序列变成有序序列


简单例子

初学者们可能无法看懂上面的描述,不过没关系,让我们来举个栗子:

待排序序列:    5   3   7   6   9   1   2第一轮排序:    2   3   1   5   9   6   7第二轮排序:    1   2   3   5   7   6   9第三轮排序:    1   2   3   5   6   7   9第四轮排序:    1   2   3   5   6   7   9

第一轮快速排序,把上面这个序列分成了两个部分,然后5作为关键值在中间
第二轮快速排序,分别再对之前的两个序列再进行操作,2和9分别是关键值
第三轮中,只有一个值的序列不再进行操作,现在灰色区域的序列
第四轮中,每个部分都只有一个值了,排序结束

由此可见,快速排序要做的事,就是用二分的思想,每次都将待处理的序列分成两个部分,并且保证其中一个序列的值都大于另一个序列的值,显然,将本操作执行完之后,序列自然就都是有序的了,那剩下的问题只有,每轮的操作该如何实现了。


时间复杂度的讨论

上面的介绍中已经提到了,快速排序算法的时间复杂度是O(nlogn),现在我们来简单地分析一下:

可见,虽然每轮操作中,待处理的序列段会逐渐变多,但需要处理的数字的数量是不会增加的,所以只要让操作的时间复杂度都是O(n) 就行了//(此处的n是每段待处理的序列中数值的数量),这样就能保证每轮的时间复杂度都是O(n)。

接着再来讨论算法需要执行几轮:在数列足够乱序的情况下,我们假设每个部分操作完了之后,都会产生左右两个序列,即构造成一个满二叉树,在这个前提下,我们执行n轮,就能解决2的n次方个数的排序。所以只需进行logn次操作就能得到答案。当然,这个前提有点苛刻。但在平均情况(随机序列)下,执行的轮数都是logn次,所以,我们说快速排序的平均时间复杂度是O(nlogn)。

有最好的情况,肯定也有最坏的情况,对快速排序来说,最坏的情况就是待排序的数组本身有序(顺序逆序都是),每次操作的时候,都会出现所有的数都在关键值的同一边的尴尬的情况,使得这个二叉树成了一个单向链表,那么操作的轮数就是n轮了,在这种极端的情况下,快速排序的时间复杂度就是O(n^2)了。


每轮快排的具体实现

讲到这里,相信大家都还有一个疑惑,那就是:每轮操作该如何执行? 我们要做到以O(n)的时间复杂度,做到让序列分成两个部分,该如何实现呢?

其实这个实现起来并不困难,举个栗子:

初始序列:5   3   7   6   9   1   2    我们选中第一个数字5作为关键值,然后先从后往前找比关键值小的数5   3   7   6   9   1   2此时我们找到了数字2,它比关键数5要大,于是我们让它和关键数交换2   3   7   6   9   1   5关键数到了后面,我们锁定5,转换方向,再从前面开始找比5大的数2   3   7   6   9   1   5   此时我们找到了数字7,交换2   3   5   6   9   1   7    之后再转换方向,从上一次找到的位置从后往前找比关键值小的数7,交换2   3   1   6   9   5   7    与1交换,再从1往后找比5大的数,找到了6,交换2   3   1   5   9   6   7    5之后,6之前已经没有比关键字大的数了,结束

这样子一轮操作就结束了,我们也确实在O(n)的时间复杂度内,实现将数组分成两个序列了,看着这个栗子是不是有点天昏地转的感觉?哈哈,没事的,习惯就好了。

用语言来描述每轮的操作就是:锁定一个数为关键数,然后先从尾部往前找比第一个比关键数小的数,找到了交换,没找到结束,然后换方向,从刚刚交换的位置再往后找,找到第一个比关键数大的数,交换,直到没找到符合条件的数为止,本轮操作结束。


代码

    #include<cstdio>      #include<iostream>      using namespace std;      int a[100005];      void swap(int n,int m)      {          int temp=a[n];          a[n]=a[m];          a[m]=temp;      }      int partition(int p,int q)      {          int n=q,s=1;//n为临时变量,s为记录方向的变量          while(p!=q)//只要p和q两变量不相等,就代表没找完          {              if( s && a[n]<a[p]) //s判断方向              {                  s=!s;//找到了,改变方向                  swap(n,p);//交换                  n=++p;              }              else if( s && a[n]>=a[p])//小于的话移一位              {                  n=--q;              }              else if( !s && a[n]>a[q])//与上面同理              {                  s=!s;                  swap(n,q);                  n=--q;              }              else if( !s && a[n]<=a[q])              {                  n=++p;              }          }          return n;      }      void qsort(int p,int q)      {          int j=partition(p,q);//进行划分操作          //两个部分再次进入递归          if(p!=j) qsort(p,j-1);          if(q!=j) qsort(j+1,q);      }      int main()      {          int t;          scanf("%d",&t);          int i=0;          for(i=0;i<t;i++)//输入数据          {              scanf("%d",&a[i]);          }          qsort(0,t-1);//进行快速排序          for(i=0;i<t;i++)//输出数据          {              printf("%d ",a[i]);          }          return 0;      } 
0 0
原创粉丝点击