最近不忙,了解下前端数据结构和算法,因看到不同的实现方法,故单独写一篇记录快速排序,便于日后回顾。
参考书籍:《学习JavaScript数据结构与算法》第2版; 《数据结构与算法JavaScript描述》第1版
方法一:
《学习JavaScript数据结构与算法》
(1) 首先,从数组中选择中间一项作为主元。 (取数组中间项)
(2) 创建两个指针,左边一个指向数组第一个项,右边一个指向数组后一个项。移动左指 针直到我们找到一个比主元大的元素,接着,移动右指针直到找到一个比主元小的元素,然后交 换它们,重复这个过程,直到左指针超过了右指针。这个过程将使得比主元小的值都排在主元之 前,而比主元大的值都排在主元之后。这一步叫作划分操作。
(3) 接着,算法对划分后的小数组(较主元小的值组成的子数组,以及较主元大的值组成的 子数组)重复之前的两个步骤,直至数组已完全排
代码实现:
function quickSort(arr) {
// arr 数组,left 左指针,right 右指针
function sort(arr, left, right) {
if (arr.length == 0) {
return []
}
// 获取主元,并划分
var index = partition(arr, left, right);
// 左侧重新划分,递归调用
if (left < index - 1) {
sort(arr, left, index - 1)
}
// 右侧重新划分,递归调用
if (right > index) {
sort(arr, index, right)
}
}
//划分过程,取一个数据中间值,使左边的值都小于它,右边的值都大于它
function partition(arr, left, right) {
// 取中间值
var pivot = arr[Math.floor((left + right) / 2)];
while (left <= right) {
// 循环找到左边大于pivot的值的索引
while (arr[left] < pivot) {
left++
}
// 循环找到右边小于pivot的值的索引
while (arr[right] > pivot) {
right--
}
// 交换位置,左指针右移,右指针左移
if (left <= right) {
[arr[left], arr[right]] = [arr[right], arr[left]];
left++;
right--;
}
}
//
return left;
}
// 调用
sort(arr, 0, arr.length - 1);
return arr;
}
方法二:
《数据结构与算法JavaScript描述》
(1) 选择一个基准元素,将列表分隔成两个子序列;(取数组第一项)
(2) 对列表重新排序,将所有小于基准值的元素放在基准值的前面,所有大于基准值的元 素放在基准值的后面;
(3) 分别对较小元素的子序列和较大元素的子序列重复步骤 1 和 2
代码实现:
function quickSort2(list) {
if (list.length == 0) {
return [];
}
var lesser = []; // 小于基准的数组
var greater = []; // 大于基准的数组
var pivot = list[0]; // 取第一个值为基准
for (var i = 1; i < list.length; i++) {
if (list[i] < pivot) {
lesser.push(list[i]);
} else {
greater.push(list[i]);
}
}
return quickSort2(lesser).concat(pivot, quickSort2(greater));
}
测试代码:
// 先生成一个随机数组
function getRandomArr(n) {
var arr = [];
for (var i = 0; i < n; i++) {
arr.push(Math.floor(Math.random() * (n + 1)))
}
return arr;
}
var randomArr = getRandomArr(100000);
// 调用方法一
let randomArr1 = JSON.parse(JSON.stringify(randomArr));
console.log(`排序前`);
console.log(randomArr1);
console.log(`排序后`);
let startTime = new Date();
console.log(quickSort(randomArr1));
console.log(`耗时: ${new Date() - startTime}ms`)
// 调用方法二
let randomArr2 = JSON.parse(JSON.stringify(randomArr));
console.log(`排序前`);
console.log(randomArr2);
console.log(`排序后`);
let startTime2 = new Date();
console.log(quickSort2(randomArr2));
console.log(`耗时: ${new Date() - startTime2}ms`)
结果发现,当数组长度为1,000时,两种方法耗时均为10ms左右,没有明显差异;当数组长度为10,000+时,方法一比方法二耗时更短;当数组长度100,000时,两种方法的耗时差更明显。因此选择数组中间项作比直接选择数组第一项为pivot,效率更高。