# 2024q1 Homework2 (quiz1+2)
contributed by < `yenshipotato` >
## 2024q1 第 1 週測驗題 - 測驗 1
### 延伸問題 1.解釋上述程式碼的運作原理
測驗 1 的 `quick_sort` 使用堆疊 begin 和 end 來保存每個子佇列的範圍,並在迴圈中不斷地處理這些子佇列,直到所有的子佇列都排序好為止。
```c
node_t *begin[max_level], *end[max_level];
```
每一次迭代會從堆疊 begin 和 end 中分別取出鏈結串列的起始與結束位置,並將它們分別指派給 L 與 R,這兩者代表著子佇列的起始與結尾。
```c
node_t *L = begin[i], *R = end[i];
```
若子佇列有效 (即 `L` 不等於 `R`) ,則將 `L` (子佇列的起始) 指定為 `pivot`,然後透過 `while(p)` 迴圈來掃描當前正在處理的子佇列,並將 `value` 大於 `pivot` 的節點放置到 `right` 中,反之則放置到 `left` 中。最後將 `left` 與 `right` 中存放的首節點與尾節點分別放入堆疊 `begin` 與 `end`
```c
node_t *pivot = L;
value = pivot->value;
node_t *p = pivot->next;
pivot->next = NULL;
while (p) {
node_t *n = p;
p = p->next;
list_add(n->value > value ? &right : &left, n);
}
begin[i] = left;
end[i] = list_tail(&left);
begin[i + 1] = pivot;
end[i + 1] = pivot;
begin[i + 2] = right;
end[i + 2] = list_tail(&right);
left = right = NULL;
i += 2;
```
```graphviz
digraph NumberSequence {
node [shape=s, fontcolor=black];
edge [color=gray, arrowsize=0.5];
rankdir=LR;
4 -> 7;
7 -> 6;
6 -> 1;
1 -> 5;
5 -> 2;
}
```
若子佇列無效 (即 `L` 等於 `R` ),則可將排序好的佇列加入 `result`
### 提出改進方案並予以實作。
* pivot的選擇
### 延伸問題 2.使用 Linux 核心風格的 List API 改寫上述程式碼,並針對鏈結串列,提出可避免最差狀況的快速排序實作,應設計效能評比的測試程式。
## 2024q1 第 1 週測驗題 - 測驗 2
### 延伸問題 1.解釋上述程式碼的運作原理
主要執行 Timsort 算法的函式為 `timsort`
執行前會先將佇列轉換成 `NULL` 結尾的單向鏈結串列。
```c
/* Convert to a null-terminated singly-linked list. */
head->prev->next = NULL;
```
接者,在迴圈中,通過 `find_run` 尋找下一個適合的 run(遞增或遞減序列)。找到 run 後,將其加入到堆疊中,並將 `list` 指向下一個未排序的節點。 `merge_collapse` 合併堆疊中的 run 並將其縮小
```c
do {
/* Find next run */
struct pair result = find_run(priv, list, cmp);
result.head->prev = tp;
tp = result.head;
list = result.next;
stk_size++;
tp = merge_collapse(priv, cmp, tp);
} while (list);
```
`merge_final` 進行最終的合併操作,將所有 run 合併成一個有序的佇列。
### 提出改進方案並予以實作。
### 延伸問題 2.使用 Linux 核心風格的 List API 改寫上述程式碼,並針對鏈結串列,提出可避免最差狀況的快速排序實作,應設計效能評比的測試程式。
## 2024q1 第 2 週測驗題 - 測驗 1
### 延伸問題 1.解釋上述程式碼的運作原理
程式主要的邏輯在 `buildTree` 中,會接受一個 preorder 佇列與 inorder 佇列,並據此建成一棵二元樹。
走訪 inorder 佇列中節點的值,將每個節點添加到對應 in_heads 的雜湊表中。
```c
for (int i = 0; i < inorderSize; i++)
node_add(inorder[i], i, inorderSize, in_heads);
```
`dfs` 通過在 inorder 佇列中查找根節點的索引,將 inorder 佇列劃分為左子樹和右子樹的範圍,並遞迴地構建左子樹和右子樹。最後,返回構建好的 TreeNode 節點。
```c
if (in_low > in_high || pre_low > pre_high)
return NULL;
struct TreeNode *tn = malloc(sizeof(*tn));
tn->val = preorder[pre_low];
int idx = find(preorder[pre_low], size, in_heads);
tn->left = dfs(preorder, pre_low + 1, pre_low + (idx - in_low), inorder,
in_low, idx - 1, in_heads, size);
tn->right = dfs(preorder, pre_high - (in_high - idx - 1), pre_high, inorder,
idx + 1, in_high, in_heads, size);
return tn;
```
### 撰寫完整的測試程式,指出其中可改進之處並實作
### 延伸問題 2.在 Linux 核心的 cgroups 相關原始程式碼,找出 pre-order walk 程式碼並探討
## 2024q1 第 2 週測驗題 - 測驗 2
### 延伸問題 1.解釋上述程式碼的運作原理
### 撰寫完整的測試程式,指出其中可改進之處並實作
### 延伸問題 2.在 Linux 核心找出 LRU 相關程式碼並探討
## 2024q1 第 2 週測驗題 - 測驗 3
### 延伸問題 1.解釋上述程式碼的運作原理
程式主要的邏輯在 `buildTree` 中。
首先, `find_nth_bit` 會檢查 n 是否大於或等於 `size`,如果是,直接返回 size,因為超出了搜尋範圍。
```c
if (n >= size)
return size;
```
如果 `small_const_nbits` 為真,則使用位元操作直接從 addr 中查找第 n 個設置位元,並返回該位元的索引。
```c
if (small_const_nbits(size)) {
unsigned long val = *addr & GENMASK(size - 1, 0);
return val ? fns(val, n) : size;
}
```
如果 `size` 不是一個小的常量,則調用 `FIND_NTH_BIT`來進行搜尋。
```c
return FIND_NTH_BIT(addr[idx], size, n);
```
`FIND_NTH_BIT` 使用迴圈遍歷位元陣列,直到搜尋完所有的位元。
### 延伸問題 2.在 Linux 核心原始程式碼找出 find_nth_bit 的應用案例,並予以解說和分析,需要涵蓋 CPU affinity 相關的原始程式碼。