详谈内部排序之快速排序

快速排序

交换排序:

​ 两两比较待排序记录的关键码,如果发生逆序(即排列顺序与排序后的次序正好相反),则交换之,直到所有记录都排好序为止 。

​ 交换排序的主要算法有:

​ (1) 冒泡排序

​ (2) 快速排序


快速排序基本思想:
(1) 基本思想:

​ 通过一趟排序将待排序列以枢轴为标准划分成两部分,使其中一部分记录的关键字均比另一部分小,再分别对这两部分进行快速排序,以达到整个序列有序。

​ 通常取第一个记录的值为基准值或枢轴。

(2) 具体做法:

​ 附设两个指针low和high,初值分别指向第一个记录和最后一个记录,设枢轴为key;

​ 1.从high 所指位置起向前搜索,找到第一个不大于基准值的记录与枢轴记录相互交换;

​ 2.从low 所指位置起向后搜索,找到第一个不小于基准值的记录与枢轴记录相互交换。

​ 3.重复这两步直至为止。


快速排序的过程:

例:以关键字序列(256,301,751,129,937,863,742,694,076,438)为例

​ 写出执行快速算法的各趟排序结束时,关键字序列的状态。

初始队列256301751129937863742694076438
第一趟【076129】256【751937863742694301438】
第二趟076129256【438301694742】751【863937】
第三趟076129256【301】438【694742】751863937
第四趟076129256301438694742751863937
详细讲解:

​ 首先我们默认每一组数据的枢轴为它第一个元素。即将初始队列中的第一个元素:256设置为枢轴。

​ 我们这里将,low或high的值改变到其他位置后,将low或high设为0,以方便辨别每一步步骤。

第一趟:

将low = 256,high = 438,key = 256;

先从high处开始:

① key < high ,所以位置不变,将 high- - 得:low = 256,high = 076

② key > high ,所以交换位置,将 low= high,同时将high处的值设为0得:low = 076,high = 0

即:

076 301 751 129 937 863 742 694 0 438


当high的位置改变后,再从low处开始:

③ key > low ,所以位置不变,将 low = 301,high = 0

④ key < low ,所以交换位置,将 low = 0,high = 301

即:

076 0 751 129 937 863 742 694 **301 ** 438

当low的位置改变后,再从high处开始:

⑤ key < high ,所以位置不变,low = 0,high = 694

⑥ key < high ,所以位置不变,low = 0,high = 742

⑦ key < high ,所以位置不变,low = 0,high = 863

⑧ key < high ,所以位置不变,low = 0,high = 937

⑨ key < high ,所以位置不变,low = 0,high = 129

⑩ key > high ,所以交换位置,low = 129,high = 0

即:

076 129 751 0 937 863 742 694 301 438


当high的位置改变后,再从low处开始:

⑪ key > low ,所以位置不变,low = 751,high = 0

⑫key < low ,所以交换位置,low = 0 ,high = 751

076 129 0 751 937 863 742 694 301 438

当low和high都搜算到一个位置的时候,说明第一趟查询结束,所以将low和high所指的地方赋值为key=256;

得到 076 129 256 751 937 863 742 694 301 438

由上可知:

枢轴256 左边的都是小于256的,在枢轴256 右边的都是大于256的。

之后再将左边和右边分为两个序列,分别进行如上的快速排序步骤。

之后的步骤只需重复上面的步骤,直到全部排列完成。


一趟快速排序算法描述

int Partition (Elem R[ ], int low, int high){
     R[0] = R[low];   pivotkey = R[low].key;  
     while (low < high) { //从两端交替向中间扫描
         while (low < high && R[high].key >= pivotkey) - - high;
         R[low] = R[high];  //将比枢轴记录小的移到低端
         while (low < high && R[low].key <= pivotkey)  + + low;
         R[high] = R[low];  //将比枢轴记录大的移到高端
    } 
    R[low] = R[0];   //枢轴记录到位
    return low;    //返回枢轴位置
} 

快速排序核心算法描述

void QSort ( Elem R[ ], int low, int high ){  //对序列R[low...high]进行快速排序
   if (low < high-1) {    //长度大于1
      pivot = Partition( L,low,high);  //将R[low..high]一分为二
      QSort(L,low, pivot-1);    //对低子表递归排序,pivo是枢轴
      QSort(L, pivot+1, high);   // 对高子表递归排序
      }
} 
void QuickSort(Elem R[], int n){   //对记录序列进行快速排序
     QSort(R, 1, n);
} 


快速排序算法分析:

​ •可以证明,函数Quicksort的平均计算时间是O(nlog2n)。实验结果表明:就平均计算时间而言,快速排序是我们所讨论的所有内排序方法中最好的一个。

​ •快速排序是递归的,需要有一个栈存放每层递归调用时的指针和参数(新的low和high)。最大递归调用层次数与递归树的深度一致,理想情况为 。因此,要求存储开销为 O(log2n)。

•最好情况:

​ 如果每次划分对一个对象定位后,该对象的左侧子序列与右侧子序列的长度相同,则下一步将是对两个长度减半的子序列进行排序,这是最理想的情况。此时,快速排序的趟数最少。

•最坏情况:

​ 即待排序对象序列已经按其关键码从小到大排好序的情况下,其递归树成为单支树,每次划分只得到一个比上一次少一个对象的子序列。这样,必须经过 n-1 趟才能把所有对象定位,而且第 i 趟需要经过 n-i 次关键码比较才能找到第 i 个对象的安放位置,总的关键码比较次数将达到n^2/2


快速排序的性能分析:

时间效率:O(nlog2n) —因为每趟确定的元素呈指数增加

空间效率:O(log2n)—因为算法的递归性,要用到栈空间

稳 定 性:不稳定 —因为可选任一元素为支点。


思考问题:快速排序是否真的比任何排序算法都快?

答:——基本上是!因为每趟可以确定的数据元素是呈指数增加的!

设每个子表的支点都在中间(比较均衡),则:

第1趟比较,可以确定1个元素的位置;

第2趟比较(2个子表),可以再确定2个元素的位置;

第3趟比较(4个子表),可以再确定4个元素的位置;

第4趟比较(8个子表),可以再确定8个元素的位置;

​ ……

综上只需 ( └ log2n ┘ + 1 ) 趟便可排好序。

​ 而且,每趟需要比较和移动的元素也呈指数下降,加上编程时使用了交替逼近技巧,更进一步减少了移动次数,所以速度特别快。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值