快速排序就是个二叉树的前序遍历,归并排序就是个二叉树的后序遍历。(听说高手都是这么想的)
所以先打点基础吧。
快排算法思路
代码框架
1 2 3 4 5 6 7 8 9 10 11
| const sort = function(nums, lo, hi) { if (lo >= hi) { return; } let p = partition(nums, lo, hi); sort(nums, lo, p - 1); sort(nums, p + 1, hi); };
|
对比二叉树前序遍历
1 2 3 4 5 6 7 8 9 10 11
| const traverse = function(root) { if (root === null) { return; } console.log(root.val); traverse(root.left); traverse(root.right); };
|
一句话总结快速排序:快速排序是先将一个元素排好序,然后再将剩下的元素排好序。
快速排序的核心无疑是 partition
函数, partition
函数的作用是在 nums[lo..hi]
中寻找一个切分点 p
,通过交换元素使得 nums[lo..p-1]
都小于等于 nums[p]
,且 nums[p+1..hi]
都大于 nums[p]
一个元素左边的元素都比它小,右边的元素都比它大,所以 partition
函数干的事情,其实就是把 nums[p]
这个元素排好序了。
从二叉树的视角,我们可以把子数组 nums[lo..hi]
理解成二叉树节点上的值,sort
函数理解成二叉树的遍历函数。
partition
函数每次都将数组切分成左小右大两部分,恰好和二叉搜索树左小右大的特性吻合。
欸☝️🤓,那快速排序的过程可不就是是一个构造二叉搜索树的过程。
但是二叉搜索树不平衡的极端情况,需要引入随机性,比如洗牌算法。当然这种东西就不是现在的我能碰瓷的了,随便记一下就好了。
快速排序代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| const quickSort = function(nums) { sort(nums, 0, nums.length - 1); };
const sort = function(nums, lo, hi) { if (lo >= hi) { return; } let p = partition(nums, lo, hi); sort(nums, lo, p - 1); sort(nums, p + 1, hi); };
const partition = function(nums, lo, hi) { let pivot = nums[lo]; let i = lo + 1, j = hi; while (i <= j) { while (i < hi && nums[i] <= pivot) { i++; } while (j > lo && nums[j] > pivot) { j--; }
if (i >= j) { break; } [nums[i], nums[j]] = [nums[j], nums[i]]; } [nums[lo], nums[j]] = [nums[j], nums[lo]]; return j; };
|