【思维】快速排序
2011-04-14 00:34 BlueDream 阅读(1172) 评论(0) 编辑 收藏 举报算法定义
快速排序是一种排序算法,由C. A. R. Hoare所发展的,以平均效能来说,排序 n 个项目要Θ(n log n)次比较。然而,在最坏的效能下,它需要Θ(n2)次比较。一般来说,快速排序实际上明显地比其他Θ(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来,且在大部分真实世界的数据,可以决定设计的选择,减少所需时间的二次方项之可能性。
算法描述
快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。
步骤为:
- 从数列中挑出一个元素,称为 "基准"(pivot),
- 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
- 递归地(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);
// 首先将首元素做为枢轴
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, 0, 7);
alert(arr);
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, 0, 7);
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, 0, 7);
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, 0, 7);
alert(arr);
其余参考:
http://www.ruanyifeng.com/blog/2011/04/quicksort_in_javascript.html?20110404221633