博客
关于我
【算法】————5、快速排序
阅读量:172 次
发布时间:2019-02-28

本文共 3296 字,大约阅读时间需要 10 分钟。

算法简介

快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

算法描述和实现

快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

  • <1>.从数列中挑出一个元素,称为 "基准"(pivot);
  • <2>.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • <3>.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

Javascript代码实现:

/*方法说明:快速排序@param  array 待排序数组*/function quickSort(array, left, right) {    console.time('1.快速排序耗时');    if (Object.prototype.toString.call(array).slice(8, -1) === 'Array' && typeof left === 'number' && typeof right === 'number') {        if (left < right) {            var x = array[right], i = left - 1, temp;            for (var j = left; j <= right; j++) {                if (array[j] <= x) {                    i++;                    temp = array[i];                    array[i] = array[j];                    array[j] = temp;                }            }            quickSort(array, left, i - 1);            quickSort(array, i + 1, right);        }        console.timeEnd('1.快速排序耗时');        return array;    } else {        return 'array is not an Array or left or right is not a number!';    }}

方法2:

//方法二var quickSort2 = function(arr) {    console.time('2.快速排序耗时');  if (arr.length <= 1) { return arr; }  var pivotIndex = Math.floor(arr.length / 2);  var pivot = arr.splice(pivotIndex, 1)[0];  var left = [];  var right = [];  for (var i = 0; i < arr.length; i++){    if (arr[i] < pivot) {      left.push(arr[i]);    } else {      right.push(arr[i]);    }  }console.timeEnd('2.快速排序耗时');  return quickSort2(left).concat([pivot], quickSort2(right));};var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];console.log(quickSort(arr,0,arr.length-1));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]console.log(quickSort2(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

JAVA实现:

public static void quickSort(int[] arr){    qsort(arr, 0, arr.length-1);}private static void qsort(int[] arr, int low, int high){    if (low < high){        int pivot=partition(arr, low, high);        //将数组分为两部分        qsort(arr, low, pivot-1);                   //递归排序左子数组        qsort(arr, pivot+1, high);                  //递归排序右子数组    }}private static int partition(int[] arr, int low, int high){    int pivot = arr[low];     //枢轴记录    while (low
=pivot) --high; arr[low]=arr[high]; //交换比枢轴小的记录到左端 while (low

法性能/复杂度

可以看出,每一次调用partition()方法都需要扫描一遍数组长度(注意,在递归的时候这个长度并不是原数组的长度n,而是被分隔出来的小数组,即n*(2^(-i))),其中i为调用深度。而在这一层同样长度的数组有2^i个。那么,每层排序大约需要O(n)复杂度。而一个长度为n的数组,调用深度最多为log(n)层。二者相乘,得到快速排序的平均复杂度为O(n ㏒n)。

通常,快速排序被认为是在所有同数量级的排序方法中,平均性能最好。

从代码中可以很容易地看出,快速排序单个栈的空间复杂度不高,每次调用partition方法时,其额外开销只有O(1)。所以,最好情形下快速排序空间复杂度大约为O(㏒n)。

算法优化

上面这个快速排序算法可以说是最基本的快速排序,因为它并没有考虑任何输入数据。但是,我们很容易发现这个算法的缺陷:这就是在我们输入数据基本有序甚至完全有序的时候,这算法退化为冒泡排序,不再是O(n㏒n),而是O(n^2)了。

究其根源,在于我们的代码实现中,每次只从数组第一个开始取。如果我们采用“三者取中”,即arr[low],arr[high],arr[(low+high)/2]三者的中值作为枢轴记录,则可以大大提高快速排序在最坏情况下的性能。但是,我们仍然无法将它在数组有序情形下的性能提高到O(n)。还有一些方法可以不同程度地提高快速排序在最坏情况下的时间性能。

此外,快速排序需要一个递归栈,通常情况下这个栈不会很深,为log(n)级别。但是,如果每次划分的两个数组长度严重失衡,则为最坏情况,栈的深度将增加到O(n)。此时,由栈空间带来的空间复杂度不可忽略。如果加上额外变量的开销,这里甚至可能达到恐怖的O(n^2)空间复杂度。所以,快速排序的最差空间复杂度不是一个定值,甚至可能不在一个级别。

决这个问题,我们可以在每次划分后比较两端的长度,并先对短的序列进行排序(目的是先结束这些栈以释放空间),可以将最大深度降回到O(㏒n)级别。

  • 最佳情况:T(n) = O(nlogn)
  • 最差情况:T(n) = O(n2)
  • 平均情况:T(n) = O(nlogn)

 

转载地址:http://bvwj.baihongyu.com/

你可能感兴趣的文章
mysql Timestamp时间隔了8小时
查看>>
Mysql tinyint(1)与tinyint(4)的区别
查看>>
mysql union orderby 无效
查看>>
mysql v$session_Oracle 进程查看v$session
查看>>
mysql where中如何判断不为空
查看>>
MySQL Workbench 使用手册:从入门到精通
查看>>
mysql workbench6.3.5_MySQL Workbench
查看>>
MySQL Workbench安装教程以及菜单汉化
查看>>
MySQL Xtrabackup 安装、备份、恢复
查看>>
mysql [Err] 1436 - Thread stack overrun: 129464 bytes used of a 286720 byte stack, and 160000 bytes
查看>>
MySQL _ MySQL常用操作
查看>>
MySQL – 导出数据成csv
查看>>
MySQL —— 在CentOS9下安装MySQL
查看>>
MySQL —— 视图
查看>>
mysql 不区分大小写
查看>>
mysql 两列互转
查看>>
MySQL 中开启二进制日志(Binlog)
查看>>
MySQL 中文问题
查看>>
MySQL 中日志的面试题总结
查看>>
mysql 中的all,5分钟了解MySQL5.7中union all用法的黑科技
查看>>