排序相关算法
2019年08月08日
选择排序
首先,找到数组中最小的那个元素,其次,将它和数组的第一个元素交换位置。再次,在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。
特点:
- 运行时间和输入无关
- 数据移动是最少的
1function sort(arr) { 2 const len = arr.length; 3 for (let i = 0; i < len; i++) { 4 let min = i; 5 for (let j = i; j < len; j++) { 6 if (less(arr, j, min)) min = j; 7 } 8 exch(arr, i, min); 9 } 10}
插入排序
循环数组,保证当前索引左边的所有元素都是有序的,比较当前索引和前面有序元素,将此索引插入至合适的位置。
特点为:对部分有序的数组排序速度较快
1function sort(arr) { 2 const len = arr.length; 3 for (let i = 1; i < len; i++) { 4 for (let j = i; j > 0 && less(arr, j, j - 1); j--) { 5 exch(arr, j, j - 1); 6 } 7 } 8}
归并排序
递归地将数组分成两半分别排序,然后再将结果归并起来。
有点是保证将任意长度为 N 的数组排序所需时间和 NlogN 成正比;缺点是所需的额外空间和 N 成正比。
1// 辅助数组 2let aux; 3 4function merge(arr, lo, mid, hi) { 5 // copy 6 for (let k = lo; k <= hi; k++) { 7 aux[k] = arr[k]; 8 } 9 10 let i = lo, 11 j = mid + 1; 12 for (let k = lo; k <= hi; k++) { 13 if (i > mid) { 14 // 左半边用尽 15 arr[k] = aux[j++]; 16 } else if (j > hi) { 17 // 右半边用尽 18 arr[k] = aux[i++]; 19 } else if (less(aux, i, j)) { 20 // 左边小 21 arr[k] = aux[i++]; 22 } else { 23 // 右边小 24 arr[k] = aux[j++]; 25 } 26 } 27} 28 29function _sort(arr, lo, hi) { 30 if (lo >= hi) return; 31 if (lo + 1 === hi) { 32 if (less(arr, hi, lo)) exch(arr, hi, lo); 33 return; 34 } 35 36 const mid = lo + Math.floor((hi - lo) / 2); 37 _sort(arr, lo, mid); 38 _sort(arr, mid + 1, hi); 39 merge(arr, lo, mid, hi); 40} 41 42function sort(arr) { 43 const len = arr.length; 44 45 // 初始化辅助数组 46 aux = new Array(len); 47 48 _sort(arr, 0, len - 1); 49}
快速排序
通过切分将数组分成两个子数组,切分后左半边数组不大于切分点,右半边数组不小于切分点,然后再递归地排序。
特点:
- 原地排序
- 长度为 N 的数组排序所需的时间和 NlogN 成正比
- 在切分不平衡时是低效的,所以一开始可打乱数组
1// 打乱数组 2function random(arr) { 3 const len = arr.length; 4 for (let i = 0; i < len; i++) { 5 exch(arr, i, Math.floor(Math.random() * len)); 6 } 7} 8 9// 切分 10function partition(arr, lo, hi) { 11 let i = lo, 12 j = hi + 1; 13 while (true) { 14 while (less(arr, ++i, lo)) { 15 if (i === hi) break; 16 } 17 while (less(arr, lo, --j)) { 18 if (j === lo) break; 19 } 20 if (i >= j) break; 21 exch(arr, i, j); 22 } 23 exch(arr, lo, j); 24 return j; 25} 26 27function _sort(arr, lo, hi) { 28 if (lo >= hi) return; 29 if (lo + 1 === hi) { 30 if (less(arr, hi, lo)) exch(arr, hi, lo); 31 return; 32 } 33 34 // 切点 35 const p = partition(arr, lo, hi); 36 _sort(arr, lo, p - 1); 37 _sort(arr, p + 1, hi); 38} 39 40function sort(arr) { 41 random(arr); 42 const len = arr.length; 43 _sort(arr, 0, len - 1); 44}
堆排序
1// 堆下沉 2function sink(arr, i, len) { 3 while (i * 2 + 1 < len) { 4 // 下沉时和两个子节点中较大的一个交换 5 let j = i * 2 + 1; 6 if (j + 1 <= len - 1 && less(arr, j, j + 1)) j = j + 1; 7 8 if (less(arr, i, j)) { 9 exch(arr, i, j); 10 i = j; 11 } else { 12 break; 13 } 14 } 15} 16 17function sort(arr) { 18 const len = arr.length; 19 20 // 堆有序 21 for (let k = Math.floor(len / 2); k >= 0; k--) { 22 sink(arr, k, len); 23 } 24 25 // 挑选最大值 26 let n = len; 27 while (n > 1) { 28 // 交换第一个和最后一个元素,此时最后一个元素肯定就是最大值了 29 exch(arr, 0, --n); 30 // 堆长度减一后下沉 31 sink(arr, 0, n); 32 } 33}