毕业好多年了,今天同事跟HR聊起让毕业生写快速排序,我想一想,确实忘记了具体怎么写了,只知道有个思路
- 随机找数组中的一个数字m
- 比这个数字小,元素放在左边,比这个数字大的放右边。这个方法,其实是确定了m的最终位置,并且缩窄了其他数字的可能范围。
- 把小于m的的子数组,和大于m的子数组,递归进行1,2。
const arr = [3,2,1,4,5,8,6,7];
function quickSort(arr) {
qs(arr, 0, arr.length);
}
function qs(arr, left, right) {
if (left >= right) {
return;
}
const mid = left + Math.floor((right - left) / 2);
const midVal = arr[mid];
const arr1 = [], arr2 = [], arr3 = []
for (let i=left; i<=right; i++) {
const val = arr[i];
if (val < midVal) {
arr1.push(val)
} else if (val === midVal) {
arr2.push(val)
} else {
arr3.push(val)
}
}
const newArr = [...arr1, ...arr2, ...arr3];
for (let i=left; i<=right; i++) {
arr[i]
}
qs(arr, left, arr1.length -1);
qs(arr, left + arr1.length + arr2.length, right);
}
quickSort(arr)
console.log('arr sorted', arr);
大家肯定没有见过这种写法,最特殊的地方,我使用了3个数组,分别存放<m, ==m, >m的数字,比较好理解。缺点是,空间效率降低了,需要额外NLOGN空间。另外要考虑的问题是,排序的稳定性,相同的数字,顺序要与原来的一致,在这个方法里,也是可以保证的。
in-place替换我看了两种写法,感觉双指针向中间逼近的,更容易理解,还有一种是一次遍历替换,比较难想出来。
中间逼近的写法 // www.youtube.com/watch?v=Hoi…
const arr = [3,2,1,4,5,8,6,7];
function quickSort(arr) {
qs(arr, 0, arr.length-1);
}
function swap(arr, i, j) {
const t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
function qs(arr, l, r) {
if (l >= r) {
return;
}
const m = l + Math.floor((r - l) / 2);
swap(arr, m, r);
const pivot = arr[r];
let p1 = l;
let p2 = r - 1;
while( p1 < p2 ) {
while(arr[p1] < pivot) {
p1++;
}
while(arr[p2] > pivot) {
p2--;
}
if (p1 < p2) {
swap(arr, p1, p2);
}
}
swap(arr, r, p1);
qs(arr, l, p1 -1);
qs(arr, p1 + 1, r);
}
quickSort(arr)
console.log('arr sorted', arr);
交换那里看起来2层for循环,实际上,并不是N×N,最多就是2N。
另外一种写法 www.geeksforgeeks.org/quick-sort/ 比较难理解的是,双指针,左边的指针就指向,不存在的下标low-1,有可能存在一个元素和自己交换的情况。。。思想是,p1右边就是比pivot大的元素,p2往右边寻找比pivot小的元素,如果找到了,就和p1右边的大元素交换。特殊情况就是,p1右边的元素并不比pivot大,但是和p2指向的元素重叠了,这个时候,仍然认为可以交换(看代码)。实际上,应该先找到地一个比pivot大的元素,假设下标i,然后p1 = i -1, p2 = i+1,这样就好理解了。我想这段代码一般人第一次不可能写出来,是经过考虑刚好能缩短的,一种代码写法处理了多种情况。
// Function to partition the array and return the partition index
function partition(arr, low, high) {
// Choosing the pivot
let pivot = arr[high];
// Index of smaller element and indicates the right position of pivot found so far
let i = low - 1;
for (let j = low; j <= high - 1; j++) {
// If current element is smaller than the pivot
if (arr[j] < pivot) {
// Increment index of smaller element
i++;
[arr[i], arr[j]] = [arr[j], arr[i]]; // Swap elements
}
}
[arr[i + 1], arr[high]] = [arr[high], arr[i + 1]]; // Swap pivot to its correct position
return i + 1; // Return the partition index
}
// The main function that implements QuickSort
function quickSort(arr, low, high) {
if (low < high) {
// pi is the partitioning index, arr[pi] is now at the right place
let pi = partition(arr, low, high);
// Separately sort elements before partition and after partition
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
// Driver code
let arr = [10, 7, 8, 9, 1, 5];
let N = arr.length;
// Function call
quickSort(arr, 0, N - 1);
console.log("Sorted array:");
console.log(arr.join(" "));
按照我的想法改进后的版本
// Function to partition the array and return the partition index
function partition(arr, low, high) {
// Choosing the pivot
let pivot = arr[high];
// 更容易理解的版本,添加一个前面查找的过程,最坏的结果就是p = high.
let p = low;
while (arr[p] < pivot) {
p++;
}
// Index of smaller element and indicates the right position of pivot found so far
let i = p - 1; //!!!!!
for (let j = p + 1 /**!!!!*/; j <= high - 1; j++) {
// If current element is smaller than the pivot
if (arr[j] < pivot) {
// Increment index of smaller element
i++;
[arr[i], arr[j]] = [arr[j], arr[i]]; // Swap elements
}
}
[arr[i + 1], arr[high]] = [arr[high], arr[i + 1]]; // Swap pivot to its correct position
return i + 1; // Return the partition index
}
// The main function that implements QuickSort
function quickSort(arr, low, high) {
if (low < high) {
// pi is the partitioning index, arr[pi] is now at the right place
let pi = partition(arr, low, high);
// Separately sort elements before partition and after partition
quickSort(arr, low, pi - 1);
quickSort(arr, pi + 1, high);
}
}
// Driver code
let arr = [1,2,3,4,5];
let N = arr.length;
// Function call
quickSort(arr, 0, N - 1);
console.log("Sorted array:");
console.log(arr.join(" "));