将一个乱序数组重新排序,直接思考是困难的,可以转换这个问题,变成对两个已经排好序的数组进行合并的问题,这样,用上双指针用上就解决了。
这么多个排序算法,多年过去,仍然觉得merge sort是个神奇算法,把复杂问题拆分成大家都懂的简单问题,然后组装简单问题的答案,就能解决复杂问题。
参考# Learn Merge Sort in 13 minutes
简单问题:
- 单个元素排序:不用排序
- 两个元素排序:对比大小 然后 swap
复杂问题:
- N个元素排序:拆分,变成2个元素的数组,或者1个元素的数组,排序;排序好多个小数组后,再利用双指针方法合并。
const arr = [3,7,8,4,1,5,2,6];
function swap(arr, i, j) {
const t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
mergeSort(arr, 0, arr.length - 1);
function mergeSort(arr, start, end) { //
console.log(`mergeSort ${start}, ${end}`)
if (end === start) {
// One element is just sorted
} else if (end - start === 1) {
// Sorting two element is easy.
if (arr[start] > arr[end]) {
swap(arr, start, end);
}
} else {
const mid = start + Math.floor((end - start) / 2);
mergeSort(arr, start, mid);
mergeSort(arr, mid + 1, end);
merge(arr, start, mid, end);
}
}
function merge(arr, start, mid, end) {
const arr2 = [];
// two pointer merge.
let p1 = start, p2 = mid + 1;
for(;;) {
if (p1 === mid + 1) {
break;
}
if (p2 === end + 1) {
break;
}
if (arr[p1] < arr[p2]) {
arr2.push(arr[p1]);
p1++;
} else {
arr2.push(arr[p2]);
p2++;
}
}
if (p1 === mid + 1) {
while(p2 !== end + 1) {
arr2.push(arr[p2]);
p2++;
}
}
if (p2 === end + 1) {
while(p1 !== mid + 1) {
arr2.push(arr[p1]);
p1++;
}
}
for (let i=start; i<=end; i++) {
arr[i] = arr2[i-start];
}
console.log('after merge', arr)
}
对比下别人写的代码 www.geeksforgeeks.org/merge-sort/ 发现可以更精简,拆分成2个元素的情况,也可以利用merge去排序,而不是对比+swap。不过这个思考就更加深入了,一般人一开始可能并想不出来。属于polish过的代码。
另外也发现大家的思考习惯不一样。 对于递归,我一般喜欢把有直接答案的条件,放在最前面,然后return。而geeksforgeeks,反过来,把需要继续递归的放在if里面。 对于遍历,我喜欢使用
{
// init variables...
for(;;) {
// break conditions...
// contents
// change variables for next loop
}
这样有些复杂的情况,也可以统一按照这个套路写的。 用while或者do while,感觉就是不直接。
另外其实,反过来想,其实也可以不用递归的,先将数组话分成多个2元数组,然后一步一步合并,,就可以了。