快速排序的思路是拿一个数字做对比,将数组分段,较小的数字放在左侧,较大的放在右侧。
1. 比较简单的快速排序
思路
- 取数组第一个数字
- 依次对比,小于该数字的放在数组left中,大于该数字的放在right中,等于该数字的放在mid中
- 递归left和right数组,当数组中只有一个元素时结束
- 合并三个数组
代码实现
function quickSort(li) {
if (li.length < 2) return li;
let left = [],
mid = [],
right = [];
let temp = li[0]
for (let index = 0; index < li.length; index++) {
let value = li[index];
if (value > temp) {
right.push(value)
} else if (value < temp) {
left.push(value)
} else {
mid.push(value)
}
}
return quickSort(left).concat(mid, quickSort(right))
}
let testLi = new Array(100);
for (let index = 0; index < testLi.length; index++) {
testLi[index] = Math.ceil(Math.random() * 100)
}
console.log(testLi)
console.log(quickSort(testLi))
算法缺陷
- 该算法一般来说时间复杂度为O(nlogn),但是不稳定,存在一个最坏情况,当原数组时倒序的时候,时间复杂度为O(n2)。
- 由于每一次递归都会新建三个数组,所以存在一个空间复杂度O(nlogn)
\
2. 优化算法
思路
为了解决空间复杂度问题,所有操作在原属组上进行
- 取数组第一个数字和下标,放在临时变量中,称为tempIndex和tempNum
- 指针right从后向前遍历,如果大于等于tempNum,则继续向前走,否则将这个数字放在tempIndex位置,并将tempIndex改right
- 指针left从前向后遍历,如果小于tempNum,则继续向后走,否则将这个数字放在tempIndex位置,并将tempIndex改为当前位置
- 2和3在在left和right指针相遇时,也就是left等于right时结束,将临时数字tempNum放在结束的位置,这样数组就变成tempNum左侧为小于tempNum的数字,左侧为较大的数字
- 递归数组的左侧和右侧,执行1-4步骤
- 当数组只有一个元素时结束
代码实现
function quickSort(li, start, end) {
if (start >= end) return;
let left = start,
right = end;
let tempIndex = start,
temp = li[tempIndex];
while (left < right) {
while (left < right && li[right] >= temp) {
right--;
}
if (left < right) {
li[tempIndex] = li[right]
tempIndex = right;
}
while (left < right && li[left] < temp) {
left++
}
if (left < right) {
li[tempIndex] = li[left]
tempIndex = left;
}
}
li[left] = temp;
quickSort(li, start, left - 1);
quickSort(li, left + 1, end)
}
let testLi = new Array(100);
for (let index = 0; index < testLi.length; index++) {
testLi[index] = Math.ceil(Math.random() * 100)
}
console.log(testLi)
quickSort(testLi, 0, testLi.length - 1)
console.log(testLi)
算法缺陷
该算法不存在空间复杂度的问题,但是也不稳定,对于倒序数组时间复杂度为O(n2)。为了避免这个问题,在选取tempNum的时候,可以随机选取数字中的一个数字,将这个数字和第一个数字交换然后再执行排序。这样出现O(n2)的概率就极小。