归并排序
性能分析
- 时间复杂度:平均 O(nlogN)、最好 O(nlogN)、最坏 O(nlogN)
- 空间复杂度:O(N) ,需要开启额外的数组来存合并后的数组,每次递归会释放上一层合并的结果,因此内存中同一时间只会保留一个数组,最长为n
- 稳定,每次两个子数组中元素相等,都优先选择左边的元素,保证相等元素的先后顺序一致
思路分析
- 数组不断二分,直到数组的长度为1
- 合并两个有序子数组,直到构建出完整的排序数组
- 合并之后的有序,是自底向上的排序
function merge(arr1, arr2) {
const res = [];
let index1 = 0;
let index2 = 0;
while (index1 < arr1.length && index2 < arr2.length) {
if (arr1[index1] < arr2[index2]) {
res.push(arr1[index1]);
index1++;
} else {
res.push(arr2[index2]);
index2++;
}
}
const remain =
index1 < arr1.length ? arr1.slice(index1) : arr2.slice(index2);
res.push(...remain);
return res;
}
var sortArray = function(nums) {
if (nums.length < 2) {
return nums;
}
const mid = Math.floor(nums.length / 2);
const arr1 = sortArray(nums.slice(0, mid));
const arr2 = sortArray(nums.slice(mid));
return merge(arr1, arr2);
};
快速排序
性能分析
- 时间复杂度:平均 O(nlogN)、最好 O(nlogN)、最坏 O(n2),理想的情况每次都把数组分成小于基准和大于基准两侧的数据
- 空间复杂度,快排没有额外的数组空间开销,空间复杂度由递归的深度决定,平均O(logN),最坏O(n)
- 不稳定,一次分区操作中,等于基准值的数字相对位置可能发生改变
-
- [1, 3, 3, 2, 2],分区最后pivotIndex = 1,因此变成[1, 2, 3, 3, 2],第二个索引的2原来在最后一个索引,两个2的相对顺序就发生了改变
思路分析
- 每次取一个基准,把数组分成小于基准和大于基准两部分
- 递归到最后,数组只剩两个元素,这两个元素就排好序了,整个数组也有序了
- 是自顶向下的排序
function partition(nums, left, right) {
let pivot = nums[right]; // 基准值
let pivotIndex = left; // 分区指针
// pivotIndex左边都是小于基准的值
for (let i = left; i < right; i++) {
if (nums[i] < pivot) {
[nums[pivotIndex], nums[i]] = [nums[i], nums[pivotIndex]];
pivotIndex++;
}
}
// 把基准值放在中间,最后pivotIndex指向一个大于基准值的位置(或者等于right)
[nums[pivotIndex], nums[right]] = [nums[right], nums[pivotIndex]];
return pivotIndex;
}
function quickSort(nums, left, right) {
if (left >= right) {
return;
}
const pivotIndex = partition(nums, left, right);
quickSort(nums, left, pivotIndex - 1);
quickSort(nums, pivotIndex + 1, right);
}
var sortArray = function(nums) {
quickSort(nums, 0, nums.length - 1);
return nums;
};