mirror of
https://github.com/krahets/hello-algo.git
synced 2026-04-02 02:02:57 +08:00
build
This commit is contained in:
@@ -535,7 +535,7 @@ $$
|
||||
// defer mem_arena.deinit();
|
||||
const mem_allocator = mem_arena.allocator();
|
||||
var counter = try mem_allocator.alloc(usize, 10);
|
||||
std.mem.set(usize, counter, 0);
|
||||
@memset(counter, 0);
|
||||
var n = nums.len;
|
||||
// 统计 0~9 各数字的出现次数
|
||||
for (nums) |num| {
|
||||
|
||||
@@ -20,6 +20,14 @@ comments: true
|
||||
|
||||
## 11.11.1. Q & A
|
||||
|
||||
!!! question "排序算法稳定性在什么情况下是必须的?"
|
||||
|
||||
在现实中,我们有可能是在对象的某个属性上进行排序。例如,学生有姓名和身高两个属性,我们希望实现一个多级排序/
|
||||
|
||||
先按照姓名进行排序,得到 `(A, 180) (B, 185) (C, 170) (D, 170)` ;接下来对身高进行排序。由于排序算法不稳定,我们可能得到 `(D, 170) (C, 170) (A, 180) (B, 185)` 。
|
||||
|
||||
可以发现,学生 D 和 C 的位置发生了交换,姓名的有序性被破坏了,而这是我们不希望看到的。
|
||||
|
||||
!!! question "哨兵划分中“从右往左查找”与“从左往右查找”的顺序可以交换吗?"
|
||||
|
||||
不行,当我们以最左端元素为基准数时,必须先“从右往左查找”再“从左往右查找”。这个结论有些反直觉,我们来剖析一下原因。
|
||||
@@ -29,3 +37,13 @@ comments: true
|
||||
举个例子,给定数组 `[0, 0, 0, 0, 1]` ,如果先“从左向右查找”,哨兵划分后数组为 `[1, 0, 0, 0, 0]` ,这个结果是不正确的。
|
||||
|
||||
再深入思考一下,如果我们选择 `nums[right]` 为基准数,那么正好反过来,必须先“从左往右查找”。
|
||||
|
||||
!!! question "关于尾递归优化,为什么选短的数组能保证递归深度不超过 $log n$ ?"
|
||||
|
||||
递归深度就是当前未返回的递归方法的数量。每轮哨兵划分我们将原数组划分为两个子数组。在尾递归优化后,向下递归的子数组长度最大为原数组的一半长度。假设最差情况,一直为一半长度,那么最终的递归深度就是 $log n$ 。
|
||||
|
||||
回顾原始的快速排序,我们有可能会连续地递归长度较大的数组,最差情况下为 $n, n - 1, n - 2, ..., 2, 1$ ,从而递归深度为 $n$ 。尾递归优化可以避免这种情况的出现。
|
||||
|
||||
!!! question "桶排序的最差时间复杂度为什么是 $O(n^2)$ ?"
|
||||
|
||||
最差情况下,所有元素被分至同一个桶中。如果我们采用一个 $O(n^2)$ 算法来排序这些元素,则时间复杂度为 $O(n^2)$ 。
|
||||
|
||||
Reference in New Issue
Block a user