在排序的算法中,1945 年由冯诺依曼发明的归并排序(Merge Sort),是一种典型利用分治策略高效解决问题的算法。
然而,归并排序的缺陷在于其需要额外存储空间。这引发了一个问题:能不能有一种算法 ,既不依赖额外空间,又能利用分治思想进行原地排序?
快速排序正是这样一种算法。不同于归并排序,快速排序将重心放在"分"上,让"治"自然发生。
快速排序的核心原理
快速排序分为两个核心过程组成:
划分(Partition): 选择数组中的一个元素为支点(pivot),通过一次遍历,将小于等于支点的元素移到左侧,大于支点的元素移动到右侧。
递归(recursion): 对左右两侧的子数组,重复执行上述操作,直到整个数组完全有序。
选择合适的支点(pivot)
我们首先来选择支点(pivot),支点的选择对快速排序的效率影响显著。
理想情况下,支点选择数组中位数,这样能确保划分后的子数组尽量平衡,从而最大限度地发挥分治的效果。
但在实际操作中,每次都去找中位数从性能上看,似乎划不来。因此,对于随机排列的数组而言,直接选择最后一个元素作为支点,不失为一种好方法。
划分(Partition)过程
选择好支点后,我们可以对数组进行划分操作。这也是快速排序算法中最重要的部分。
在这一过程中,通过一次数组扫描并设置两个指针
i
和 j
,形成所谓的"双指针遍历",同时确保在扫描过程中满足以下条件:
-
lo, i\] 之间的元素都 `<=pivot`。
-
j, hi-1\] 之间的元素未被扫描。
我们来详细描述一下"双指针遍历"过程中的各个阶段:
1. 扫描初始化:
在扫描开始前,我们设置 i=lo-1
和 j=lo
以保持上述三个条件成立。
在初始状态下 [j, hi-1] 之间是所有未扫描的元素,[lo, i] (<=pivot
) 和 [i+1, j-1] (>pivot
) 的区间都不存在。

2. 扫描过程:
当 A[j] > pivot
时,j
的值加 1
。保证 [i+1, j-1] 之间的元素满足 >piovt
。

当 A[j] <= pivot
时,i
加 1
,交换 A[i]
和 A[j]
的值之后,j
再加 1
。
这样同时保证了 [lo, i] 之间的元素满足 <=pivot
,并且 [i+1, j-1] 之间的元素满足 >piovt
。

3. 扫描结束:
扫描完成时 j = hi
,此时我们需要将支点 A[hi]
置于正确位置。通过交换 A[hi]
与 A[i+1]
,支点就位,返回其索引。

将上述的扫描过程转化为代码是一个有趣的挑战,你可以先思考一下如何实现。这里提供一个我编写的函数,以供参考:
python
def partition(A, lo, hi):
pivot = A[hi]
i = lo - 1
for j in range(lo, hi):
if A[j] <= pivot :
i = i + 1
A[i], A[j] = A[j], A[i]
A[i + 1], A[hi] = A[hi], A[i + 1]
return i + 1
下面的动图更好地展示了双指针遍历扫描代码的执行过程:

注意:你可以在我的 github 仓库中查看源代码 quicksort01
递归:实现快速排序
我们来完成最后一步,通过递归不断地对子数组进行划分,直到每个子数组只有一个元素,此时整个数组就被排序完成。
python
def quicksort(A, lo, hi):
if lo < hi:
pivot_index = partition(A, lo, hi)
quicksort(A, lo, pivot_index - 1)
quicksort(A, pivot_index + 1, hi)
整个递归过程如下面动图所示:

详细递归执行过程的静态示意图如下:

注意:你可以在我的 github 仓库中查看源代码 quicksort01
双指针遍历
在划分过程中,我们采用了双指针技术,这是一种常见的算法策略。该技巧有以下常见步骤和策略:
- 确定指针的移动策略: 确定如何移动两个指针以达到目的。
- 计算和更新结构: 使用两个指针计算结果,并根据需要更新结果。
- 处理边界和特殊情况: 考虑两个指针到达数组边界时的情况。

双指针技术不仅用于快速排序,还广泛应用于其他算法和问题,例如:在有序数组中查找特定和的两个数;计算数组的最大/最小子数组和;检测链表中是否存在环。
我们可以思考一下:是否还有其它双指针移动策略来实现快速排序中的划分过程?
双向的双指针遍历策略
在上面的内容中,我们详细介绍了快速排序算法,并通过双指针遍历技术成功实现了算法的核心部分------划分(partition)。
这种算法不仅简单易懂,其代码量也相对较少。
但如果我们考虑到一种特殊情况:数组完全由相同的元素组成,根据先前的算法进行划分,性能就会非常糟糕。
此时,每次支点都不变动,导致划分会将长度为 n
的数组分为长度为 n-1
和 0
的两个子数组。这使得递归深度达到了 n
层,而每一层都需要 O(n)
的时间复杂度来去除一个元素,因此总的运行时间达到了 O(n^2)
。

为了解决这个问题,我们对双指针的遍历方向进行了调整,采用了双向划分策略。
双向划分(partition)过程
首先,我们选择数组的第一个元素作为支点。在划分过程中,两个指针 i
和 j
分别初始化在数组的两端。
i
从左侧开始向右扫描,直到找到一个大于等于支点的元素为止;而 j
从右侧开始向左扫描,直至遇到小于等于支点的元素。在这个过程中,我们需要确保满足以下三个条件:
-
lo, i-1\] 之间的元素都 `<=pivot`。
-
j+1, hi\] 之间的元素都 `>=pivot`。
同样,我们也来详细描述一下这种"双指针遍历"过程中的各个阶段:
1. 扫描初始化:
扫描开始之前,设置 i=lo
和 j=hi+1
,确保上述条件得以满足。

2. 扫描过程:
- 从数组左端开始,
i
向右扫描,直到遇到大于或等于支点的元素。 - 从数组右端开始,
j
向左扫描,直到遇到小于或等于支点的元素。 - 交换这两个元素,然后继续上述的扫描过程。

3. 扫描结束:
当 i
和 j
两指针相遇时,扫描结束。此时,为了将支点 A[lo] 放置在其正确的位置,我们需要交换 A[lo] 与 A[j]。完成这一操作后,返回支点的索引值。

将上述的扫描过程转化为代码是一个有趣的挑战,你可以先思考一下如何实现。这里提供一个我编写的函数,以供参考:
python
def partition(arr, low, high):
pivot = arr[low]
i = low
j = high + 1
while True:
i += 1
while i <= high and arr[i] < pivot:
i += 1
j -= 1
while arr[j] > pivot:
j -= 1
if i > j:
break
arr[i], arr[j] = arr[j], arr[i]
arr[low], arr[j] = arr[j], arr[low]
return j
值得注意的是,在 j
向左扫描时,我们并未设置 j>=low
的条件,因为支点正是 arr[low],它不可能小于自己。
注意:你可以在我的 github 仓库中查看源代码 quicksort02
重复元素数组
现在,我们再次考虑之前所提到的那种特殊情况:数组完全由相同的元素组成。
使用新的划分策略,当遇到相同元素时,扫描会停止,并交换 i
和 j
指针的值。这样的操作虽然增加了元素交换的次数,但是得到的支点使左右子数组更加均衡,从而充分利用了分治策略,使得算法的运行时间仍为 O(nlogn)
。

快速排序的优化
我们已经深入探讨了快速排序算法及其两种主要的划分策略。
这两种策略都使用了固定元素作为支点(pivot)。对于随机输入的数据,这样的方法通常都能取得良好的效果。
但是,对于特定的输入模式,例如完全升序的数组,选择第一个元素作为支点会导致划分后的子数组极其不平衡,从而影响排序效率。
优化策略1:选择更优的支点
为了确保划分后的子数组尽可能平衡,我们需要优化支点的选择策略。
计算数组的中位数可能代价昂贵。因此,一种简单而有效的方法是选择数组中的三个元素------首、尾和中点,并将其中的中值作为支点。
这样,我们能够大幅提高支点的选择质量。当然,为了进一步增强算法的鲁棒性,我们可以考虑从更多元素中选择支点。
python
def media_three(arr, lo, mid, hi):
a, b, c = arr[lo], arr[mid], arr[hi]
if (a <= b <= c) or (c <= b <= a):
return mid
elif (b <= a <= c) or (c <= a <= b):
return lo
else:
return hi
注意:你可以在我的 github 仓库中查看源代码 quicksort03
优化策略2:混合使用插入排序
对于较小的数组,插入排序往往比快速排序更为高效。因此,结合快速排序和插入排序,采用混合排序策略,可以进一步提高排序效率。
具体而言,当待排序的数组大小达到某一阈值时,我们切换到插入排序。
至于何时切换,即数组的大小阈值是多少,这与具体系统有关。
《算法》中提到,5-15 之间的值在大多数情况下都表现良好。《编程珠玑》中,建议选用 30-70 之间的值,其中 50 被认为是一个较为理想的选择。
当然,具体的阈值最好根据实际应用场景进行调整。
在以下的代码示例中,我们使用变量 M 作为这个阈值。
python
def quicksort(A, lo, hi):
M = 5
if hi <= lo + M:
insertionsort(A, lo, hi)
return
pivot_index = partition(A, lo, hi)
quicksort(A, lo, pivot_index - 1)
quicksort(A, pivot_index + 1, hi)
注意:你可以在我的 github 仓库中查看源代码 quicksort04
总结
快速排序的"快速"特性源自其独特的算法设计:精心挑选的支点和高效的双指针遍历。
在快速排序中,支点的选择扮演着关键角色。理想情况下,支点能够将数组平均分割,现分治策略的最大效率。尽管在实践中难以始终选出最佳支点,但通过合理的方法(如三数取中法),我们可以逼近理想情况,有效降低排序时间。
双指针遍历策略进一步提高了排序效率。通过在数组中移动两个指针,并在合适时刻交换元素,这种方法实现了原地排序,即无需额外空间。这是快速排序相较于其他算法(例如归并排序)的显著优势。
结合这两种策略,快速排序不仅能够在平均情况下达到接近最优的时间复杂度(O(nlogn)),而且在内存使用上也极为高效,这使得它在多种场景下仍是一种广受欢迎的排序算法。
然而,快速排序也存在局限性。在特定数据排列下(如已排序或重复元素众多的数组),其性能可能不理想,甚至可能退化至 O(n^2)。此外,极端情况下的不稳定性也值得注意。
因此,理解快速排序的局限性对于选择和应用合适的排序算法至关重要。尽管快速排序在多数情况下表现出色,但在特定应用场景中,其他排序算法可能更适合。
更多内容,欢迎关注我的公众号:dingtingli
WWH 系列文章列表:
1\] [Why - 为什么 JS 更像一门编译型语言?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454608234%26idx%3D1%26sn%3D5b2b7c62d18f01ce5dfa7607bc86a12b%26chksm%3Dfbf0b331cc873a275a5f3d42081549feb668e9d1beea7ce78125c0a547728a9cb262ff266194%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454608234&idx=1&sn=5b2b7c62d18f01ce5dfa7607bc86a12b&chksm=fbf0b331cc873a275a5f3d42081549feb668e9d1beea7ce78125c0a547728a9cb262ff266194&scene=21#wechat_redirect") \[2\] [What - 什么是依赖注入?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454608405%26idx%3D1%26sn%3D5237d417b8a7d26ff11a7fea13c42f3d%26chksm%3Dfbf0b04ecc873958d4c0764d0bf2552da9ebd36cac39b4222af1c47fcca562b8a772561146fb%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454608405&idx=1&sn=5237d417b8a7d26ff11a7fea13c42f3d&chksm=fbf0b04ecc873958d4c0764d0bf2552da9ebd36cac39b4222af1c47fcca562b8a772561146fb&scene=21#wechat_redirect") \[3\] [What - 如何清晰地理解算法复杂度 Big O?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454608780%26idx%3D1%26sn%3Dcc5a66e5653f05834a987deb805a0a01%26chksm%3Dfbf0b1d7cc8738c1b31566dcbb3daf5de950da27df4258cac0b800081b0748f6ae0f90b945b1%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454608780&idx=1&sn=cc5a66e5653f05834a987deb805a0a01&chksm=fbf0b1d7cc8738c1b31566dcbb3daf5de950da27df4258cac0b800081b0748f6ae0f90b945b1&scene=21#wechat_redirect") \[4\] [How - 不同的语言都如何处理错误?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609524%26idx%3D1%26sn%3D49a447dd1cef64fcd1f31878a954d216%26chksm%3Dfbf0b42fcc873d39c1b5734856980b9cb1dbccbdd0be93b65bce7c627454e3f464a6682241ff%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609524&idx=1&sn=49a447dd1cef64fcd1f31878a954d216&chksm=fbf0b42fcc873d39c1b5734856980b9cb1dbccbdd0be93b65bce7c627454e3f464a6682241ff&scene=21#wechat_redirect") \[5\] [How - 面向对象语言如何处理异常?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609526%26idx%3D1%26sn%3Dfba1299fe5eb1263e7fffafe46efe747%26chksm%3Dfbf0b42dcc873d3b0e380429796733109f9b031e0e0bd566f7267746fa49091ba60b2b7ccbea%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609526&idx=1&sn=fba1299fe5eb1263e7fffafe46efe747&chksm=fbf0b42dcc873d3b0e380429796733109f9b031e0e0bd566f7267746fa49091ba60b2b7ccbea&scene=21#wechat_redirect") \[6\] [Why - 为什么排序算法复杂度上限是 O(NlogN)?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609763%26idx%3D1%26sn%3D73eebf3a295e0d01ae75da17b5ec13b4%26chksm%3Dfbf0b538cc873c2e812fb71fb9316d9ec94d0e03c95847f36599cc0a16c4ccfc15b8ceda4d2a%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609763&idx=1&sn=73eebf3a295e0d01ae75da17b5ec13b4&chksm=fbf0b538cc873c2e812fb71fb9316d9ec94d0e03c95847f36599cc0a16c4ccfc15b8ceda4d2a&scene=21#wechat_redirect") \[7\] [Why - 为什么排序算法还是不够快?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454610154%26idx%3D1%26sn%3D42689ae8274db24f09fbd41a67dab8f0%26chksm%3Dfbf08ab1cc8703a7dfbbe74b997adc580f4f2d57e3f891073cb5567f669f3352162f6696ac5a%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454610154&idx=1&sn=42689ae8274db24f09fbd41a67dab8f0&chksm=fbf08ab1cc8703a7dfbbe74b997adc580f4f2d57e3f891073cb5567f669f3352162f6696ac5a&scene=21#wechat_redirect") 最近文章列表: \[1\] [看图聊算法:一个游戏让你理解二分法的本质](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609685%26idx%3D1%26sn%3D789b352a289ee9a9a5a0395448fbc0b3%26chksm%3Dfbf0b54ecc873c580fdf833e4ba6aba29771c78bc26ecca5dee7245e819e49de962aee39c35a%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609685&idx=1&sn=789b352a289ee9a9a5a0395448fbc0b3&chksm=fbf0b54ecc873c580fdf833e4ba6aba29771c78bc26ecca5dee7245e819e49de962aee39c35a&scene=21#wechat_redirect") \[2\] [看图聊算法:还是一个游戏,让你理解三分法的本质](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609698%26idx%3D1%26sn%3D7a65dc149f2c1da8ebc15e91e7d17f43%26chksm%3Dfbf0b579cc873c6f6637ceb38c800865ca5f1e36f087940a629c104ef4fc272b79b5b3dd925e%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609698&idx=1&sn=7a65dc149f2c1da8ebc15e91e7d17f43&chksm=fbf0b579cc873c6f6637ceb38c800865ca5f1e36f087940a629c104ef4fc272b79b5b3dd925e&scene=21#wechat_redirect") \[3\] [看图聊算法:为什么插入排序效率不高,却是使用频率最高的排序算法](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609789%26idx%3D1%26sn%3D4a3a2c271c6addb6cbbf92abbaed3893%26chksm%3Dfbf0b526cc873c309dc1384d09359032028bc176bc71166214a42103b3a526d601dd2849b46c%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609789&idx=1&sn=4a3a2c271c6addb6cbbf92abbaed3893&chksm=fbf0b526cc873c309dc1384d09359032028bc176bc71166214a42103b3a526d601dd2849b46c&scene=21#wechat_redirect")? \[4\] [看图聊算法:归并排序一个被教科书嫌弃的算法,我们为什么还要学](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609846%26idx%3D1%26sn%3D1e2ea7ab24c5de0e70fb4e474bf8b1dc%26chksm%3Dfbf0b5edcc873cfb2858ef544bc6cdb2eec5f1400b1355dfedd02aaae4cc586b399a45941bff%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609846&idx=1&sn=1e2ea7ab24c5de0e70fb4e474bf8b1dc&chksm=fbf0b5edcc873cfb2858ef544bc6cdb2eec5f1400b1355dfedd02aaae4cc586b399a45941bff&scene=21#wechat_redirect")? \[5\] [看图聊算法:冯·诺依曼的第一个计算机程序是怎么做出来的?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609864%26idx%3D1%26sn%3Ddc220fb01afba6abba6940e87d75a4fc%26chksm%3Dfbf0b593cc873c85d2cfca6fb318b71a129f55e3a2ed3a67181dbeff062b7fe8e9d47418687c%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609864&idx=1&sn=dc220fb01afba6abba6940e87d75a4fc&chksm=fbf0b593cc873c85d2cfca6fb318b71a129f55e3a2ed3a67181dbeff062b7fe8e9d47418687c&scene=21#wechat_redirect") \[6\] [看图聊算法:快速排序为什么快?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609959%26idx%3D1%26sn%3Da5889e55d4fd4f03f912880f7b0aa589%26chksm%3Dfbf08a7ccc87036aba727418ffa30d2a9d5a4c569ac5b71b806a719ba2f7fc45729ddca4b98f%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609959&idx=1&sn=a5889e55d4fd4f03f912880f7b0aa589&chksm=fbf08a7ccc87036aba727418ffa30d2a9d5a4c569ac5b71b806a719ba2f7fc45729ddca4b98f&scene=21#wechat_redirect") \[7\] [不刷题,不面试,来看看算法学习在编程世界中的真正价值](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454609972%26idx%3D1%26sn%3Df1efc948ce0c0466f72e98b8674f96f7%26chksm%3Dfbf08a6fcc8703799f2f61ab7a1a634a23c5d7428cc1233e34843a26e2966a9e2783bcf7f6c5%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454609972&idx=1&sn=f1efc948ce0c0466f72e98b8674f96f7&chksm=fbf08a6fcc8703799f2f61ab7a1a634a23c5d7428cc1233e34843a26e2966a9e2783bcf7f6c5&scene=21#wechat_redirect") \[8\] [看图聊算法:堆排序,我们学习它可能并不是为了排序](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454610070%26idx%3D1%26sn%3Db8d0bb11971ccfc36b445a4b856fc4dd%26chksm%3Dfbf08acdcc8703db4c894a00aca1300d72787088e783e2a2419041f32b3efda6e3107d683982%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454610070&idx=1&sn=b8d0bb11971ccfc36b445a4b856fc4dd&chksm=fbf08acdcc8703db4c894a00aca1300d72787088e783e2a2419041f32b3efda6e3107d683982&scene=21#wechat_redirect") \[9\] [看图聊算法:为什么快速排序不够快?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454610078%26idx%3D1%26sn%3De29d53b55157608aafc62fb5179e053d%26chksm%3Dfbf08ac5cc8703d31a750832e4765b47e2d0555aba8ed3238af5a646fe2fb01ae085da6f1a72%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454610078&idx=1&sn=e29d53b55157608aafc62fb5179e053d&chksm=fbf08ac5cc8703d31a750832e4765b47e2d0555aba8ed3238af5a646fe2fb01ae085da6f1a72&scene=21#wechat_redirect") \[10\] [看图聊算法:为什么堆排序不够快?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454610086%26idx%3D1%26sn%3Dbbf7b60402bf74f629fb25b939032609%26chksm%3Dfbf08afdcc8703eb4b1923f92ded27aacd444d9a79fef0ab9b32934a7e484324f9f6d8a62221%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454610086&idx=1&sn=bbf7b60402bf74f629fb25b939032609&chksm=fbf08afdcc8703eb4b1923f92ded27aacd444d9a79fef0ab9b32934a7e484324f9f6d8a62221&scene=21#wechat_redirect") \[11\] [看图聊算法:为什么排序算法还是不够快?](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454610154%26idx%3D1%26sn%3D42689ae8274db24f09fbd41a67dab8f0%26chksm%3Dfbf08ab1cc8703a7dfbbe74b997adc580f4f2d57e3f891073cb5567f669f3352162f6696ac5a%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454610154&idx=1&sn=42689ae8274db24f09fbd41a67dab8f0&chksm=fbf08ab1cc8703a7dfbbe74b997adc580f4f2d57e3f891073cb5567f669f3352162f6696ac5a&scene=21#wechat_redirect") \[12\] [一张图读懂异步编程模型是如何运作的](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454610093%26idx%3D1%26sn%3D90a42b3d35b89718a8246ba2583e5fd8%26chksm%3Dfbf08af6cc8703e054bbc42a15afa40252fc554a1ae61d788e1d2d247fec736879f782708804%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454610093&idx=1&sn=90a42b3d35b89718a8246ba2583e5fd8&chksm=fbf08af6cc8703e054bbc42a15afa40252fc554a1ae61d788e1d2d247fec736879f782708804&scene=21#wechat_redirect") \[13\] [一张图读懂并发/并行/异步的区别](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454610140%26idx%3D1%26sn%3D732e27c8acaac3ffbc89f51921126345%26chksm%3Dfbf08a87cc8703915119c61bd0d54eb3c5b7a39879e3fb14bf01e0b43ed34da522b95a1720b8%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454610140&idx=1&sn=732e27c8acaac3ffbc89f51921126345&chksm=fbf08a87cc8703915119c61bd0d54eb3c5b7a39879e3fb14bf01e0b43ed34da522b95a1720b8&scene=21#wechat_redirect") \[14\] [GitHub 秘籍:设置 Git 代理,让你的代码提交变得畅通无阻](https://link.juejin.cn?target=http%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzI5NjA1MDQ4NA%3D%3D%26mid%3D2454610141%26idx%3D1%26sn%3Dbe03737d0aef55400e5eacc3a6289ddd%26chksm%3Dfbf08a86cc870390bdf2f4f2251d8f0f647eace0e8ae38ac59ac85cabf4c4daafb0585fb1ce7%26scene%3D21%23wechat_redirect "http://mp.weixin.qq.com/s?__biz=MzI5NjA1MDQ4NA==&mid=2454610141&idx=1&sn=be03737d0aef55400e5eacc3a6289ddd&chksm=fbf08a86cc870390bdf2f4f2251d8f0f647eace0e8ae38ac59ac85cabf4c4daafb0585fb1ce7&scene=21#wechat_redirect")