owned this note
owned this note
Published
Linked with GitHub
# 2024q1 Homework2 (quiz1+2)
contributed by < [ChengChaoChun](https://github.com/ChengChaoChun) >
## 2024q1 第 1 週測驗題
## 第一題
### 分析 [Optimized QuickSort — C Implementation (Non-Recursive)](https://alienryderflex.com/quicksort/)
以升序排序來說,Quicksort 的關鍵是把 pivot key 放到陣列的正確位置,也就是在 pivot key 的左側都是小於等於 pivot key,右側都是大於等於pivot key。在 Non-Recursive 版本中,新增了兩個陣列,beg 和 end。把 pivot key 放置到正確的位置後, beg 陣列會分別記錄 pivot key 左測的陣列的第一個元素的索引和 pivot key 右測陣列的第一個元素索引,而 end 陣列會分別記錄 pivot key 左測的陣列的最後一個元素的索引和 pivot key 右測的陣列的最後一個元素的索引。這個步驟取代了原本 Wikipedia 版本的函式遞迴,因為遞迴式需要時間成本的。
### 測驗
題目將原本陣列資料結構換成 linked list,來實作 Non-Recursive Quicksort。
* 原本的 beg 和 end 紀錄索引的陣列替換成了 beg 指標陣列和 end 的陣列
```
node_t *begin[max_level], *end[max_level];
node_t *result = NULL, *left = NULL, *right = NULL;
begin[0] = *list;
end[0] = list_tail(list);
```
* 建立三個指標,result,right,left。如果當前節點 n 的 value 大於 pivot 的 value 就把 n 鏈接到 right , 否則鏈接到 left 。
```
while (p) {
node_t *n = p;
p = CCCC;
list_add(n->value > value ? &right : &left, n);
}
```
* 把每個 node 都放到對應的 right 或 left 之後,依序將 left , pivot , right 分別加入到 begin 和 end 指標陣列,因為最後 result 是以這個順序來鏈接節點的。最後把 right 和 left 設置為 null ,然後接著下一輪的排序。
```
begin[i] = left;
end[i] = DDDD;
begin[i + 1] = pivot;
end[i + 1] = pivot;
begin[i + 2] = right;
end[i + 2] = EEEE;
left = right = NULL;
i += 2;
```
![螢幕擷取畫面 2024-03-09 153244](https://hackmd.io/_uploads/HJ3hx5Kpp.png)
## 2024q1 第 2 週測驗題
### 第一題
* 建立 hash table
```
struct hlist_head *in_heads = malloc(inorderSize * sizeof(*in_heads));
for (int i = 0; i < inorderSize; i++)
INIT_HLIST_HEAD(&in_heads[i]);
```
* 以 inorder 順序依序將 node 添加到 hash table。
* 參考 [Linux 核心的 hash table 實作](https://hackmd.io/@sysprog/linux-hashtable) 可以清楚了解 Linux 核心的 hash table 資料結構
```
for (int i = 0; i < inorderSize; i++)
node_add(inorder[i], i, inorderSize, in_heads);
```
* 在 node_add 函式中根據 hash 的值來添加到對應的 hash table 索引
```
static inline void node_add(int val,
int idx,
int size,
struct hlist_head *heads)
{
struct order_node *on = malloc(sizeof(*on));
on->val = val;
on->idx = idx;
int hash = (val < 0 ? -val : val) % size;
hlist_add_head(&on->node, DDDD);
}
```
* 以 DFS 的方式來建立 Binary tree,這裡的關鍵是以 preorder 的順序依次找到 tree(subtree) 的 root 在 inorder 中的位置
* 參考[C++ | Recursive & Using Map Approaches](https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solutions/2279613/c-recursive-using-map-approaches/) 以相同方法建立 Binary tree
```
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);
```
### 第二題
`cashe` 紀錄容量,當前已使用的空間,還有維護兩個資料結構,分別是 linked list 和 hash table
#### lRUCacheCreate
`cashe` 建立後如下圖
![螢幕擷取畫面 2024-03-10 022551](https://hackmd.io/_uploads/B1IlqQq6T.png)
#### lRUCacheFree
首先 traverse cashe 的 linked list,把每個 LRUNode 從 linked list 中移除並釋放記憶體空間,接著把 cashe 配置的空間回收。
#### lRUCacheGet
計算 key 的 hash 值,然後搜索 hash table 的那個 bucket。如果找到了需要把該 LRUNode 的 list_head 移動到第一個位置。 linked list 的順序表示訪問 LRUNode 順序,越晚訪問到節點放在越前面。
#### lRUCachePut
* 這個函式的前半部分與 `lRUCacheGet` 相同,如果 LRUNode 已經存在就只要更新 value
* 如果 cashe 已經滿了,把 linked list 最後一個節點移除, hash table 中的紀錄也要移除。把新加入節點鏈接在 linked list 的第一個位置,並放入 hash table 對應的 bucket 。 否則配置一個 LRUNode 空間,然後同樣把節點鏈接在 linked list 的第一個位置和放入 hash table 對應的 bucket
* 如果 cashe 已經滿了,新加入的資料使用的記憶體空間是 linked list 最後一個節點的 LRUNode 空間,也就是 Least Recently Used LRUNode 原本的空間,所以不需要 malloc 新的空間
* 如果 cashe 已經滿了,因為使用的是原本 Least Recently Used LRUNode 的空間,所以 count 不需要+1 ; 如果 cashe 還有空間,會配置一個新的 LRUNode 空間,所以 count 要+1
```
if (!cache) {
if (obj->count == obj->capacity) {
cache = list_last_entry(&obj->dhead, LRUNode, link);
list_move(&cache->link, &obj->dhead);
hlist_del(&cache->node);
hlist_add_head(&cache->node, &obj->hhead[hash]);
} else {
cache = malloc(sizeof(LRUNode));
hlist_add_head(&cache->node, &obj->hhead[hash]);
list_add(&cache->link, &obj->dhead);
obj->count++;
}
cache->key = key;
}
```