代码改变世界

【思维】快速排序

2011-04-14 00:34 by BlueDream, ... 阅读, ... 评论, 收藏, 编辑

算法定义 

快速排序是一种排序算法,由C. A. R. Hoare所发展的,以平均效能来说,排序 n 个项目要Θ(n log n)次比较。然而,在最坏的效能下,它需要Θ(n2)次比较。一般来说,快速排序实际上明显地比其他Θ(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。

算法描述 

快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。

步骤为:

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

递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。 

源码描述

function QuickSort_Once(_pnArray, _pnLow, _pnHigh) {
    
// 首先将首元素做为枢轴
    var nPivot = _pnArray[_pnLow];
    
var i = _pnLow, j = _pnHigh; // 设置两个指针,分别指向首尾处
    while(i < j) {  // 直到两个指针重合
        // 从右向左,寻找首个小于枢轴的元素
        while(_pnArray[j] >= nPivot && i < j) j--;
        _pnArray[i] 
= _pnArray[j];   // 找到后 执行替换

        
// 从左到右,寻找首个大于枢轴的元素
        while(_pnArray[i] <= nPivot && i < j) i++;
        _pnArray[j] 
= _pnArray[i];   // 找到后 执行替换
    }
    
// 至此i,j两指针重合执行同一位置,i即是新的枢轴位置
    _pnArray[i] = nPivot;
    
return i;
}
function QuickSort(_pnArray, _pnLow, _pnHigh) {
    
if(_pnLow >= _pnHigh) return;
    
// 获取枢轴
    var _nPivotIndex = QuickSort_Once(_pnArray, _pnLow, _pnHigh);
    
// 然后对被枢轴分开的两侧进行分别递归
    QuickSort(_pnArray, _pnLow, _nPivotIndex - 1);
    QuickSort(_pnArray,  _nPivotIndex 
+ 1, _pnHigh);
}

/* 排序测试 */
(
function() {
    
// 10个随机数
    var arr = [];
    
for(var i = 0; i < 10; i++) {
        arr[i] 
= Math.floor(Math.random() * 60);
    }
    document.write(
'初始值: ' + arr + '<br/>');
    QuickSort(arr, 
0, arr.length - 1);
    document.write(
'排序值: ' + arr);

})();  

另一种实现

function QuickSort(arr, start, end) {
    
if (start < end) {
        
var q = Partition(arr, start, end); // 找出分割基准点
        QuickSort(arr, start, q-1);
        QuickSort(arr, q
+1, end);
    } 
}

// 元素交换
function swap(arr, a, b) {
    
if (a == b) return
    
var temp = arr[a];
    arr[a] 
= arr[b];
    arr[b] 
= temp;
}

// 数组分割
function Partition(arr, start, end) {
    
var pivot = arr[end]; // 将数组最后一个元素作为主元
    var i = start - 1// 指定一个指针
    for (var j = start; j < end; j++) {
        
if (arr[j] <= pivot)  { // 如果当前元素小于主元
            i = i + 1;
            swap(arr, i, j);
        } 
    }
    swap(arr, i 
+ 1, end);
    
return (i + 1);
}

var arr = [2,8,7,1,3,5,6,4];
QuickSort(arr, 
07);
alert(arr);

因为如果是已经排好序的话。那么用快速选择还需要O(n^2)的时间复杂度。这样是不优的。为了防止这种情况。所以在数据量大的时候可以使用随机化版本。随机化主元就避免最坏情况发生。

随机选择排序

function QuickSort(arr, start, end) {
    
if (start < end) {
        
var q = RandomPartition(arr, start, end); // 找出分割基准点
        QuickSort(arr, start, q-1);
        QuickSort(arr, q
+1, end);
    } 
}

// 元素交换
function swap(arr, a, b) {
    
if (a == b) return
    
var temp = arr[a];
    arr[a] 
= arr[b];
    arr[b] 
= temp;
}

// 数组分割
function Partition(arr, start, end) {
    
var pivot = arr[end]; // 将数组最后一个元素作为主元
    var i = start - 1// 指定一个指针
    for (var j = start; j < end; j++) {
        
if (arr[j] <= pivot)  { // 如果当前元素小于主元
            i = i + 1;
            swap(arr, i, j);
        } 
    }
    swap(arr, i 
+ 1, end);
    
return (i + 1);
}

// 随机交换主元后再Partition
function RandomPartition(arr, start, end) {
    
var i = Math.floor(Math.random() * (end - start + 1+ start);
    swap(arr, end, i);
    
return Partition(arr, start, end);
}

var arr = [2,8,7,1,3,5,6,4];
QuickSort(arr, 
07);

alert(arr); 

其余参考:

c#与算法--快速排序

快速排序

CSDN快速排序

http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html?20110404221633 

http://jsdo.it/norahiko/oxIy/fullscreen