---
# System prepended metadata

title: 2024q1 Homework2 (quiz1+2)

---

# 2024q1 Homework2 (quiz1+2)
contributed by < [mesohandsome](https://github.com/mesohandsome) >

## 第 1 周測驗題測驗 1

### `list_tail()`

這裡使用間接指標來操作，這個函式用來找到串列的結尾，所以要不斷將指標向後更新，因此 `AAAA` 這邊為 `(*left)->next`

```graphviz
digraph G {
    node [shape = box, fontname = "Times-Roman"];
    rankdir = LR;

    indirect [label = "indirect"];
    head [label = "head"];
    node1 [label = "node1"];
    node2 [label = "node2"];

    subgraph cluster_1 {
        label = "linked list";
        head -> node1 -> node2;
    };

    indirect -> head;
}
```

```graphviz
digraph G {
    node [shape = box, fontname = "Times-Roman"];
    rankdir = LR;

    indirect [label = "indirect"];
    head [label = "head"];
    node1 [label = "node1"];
    node2 [label = "node2"];
    
    subgraph cluster_1 {
        label = "linked list";
        head -> node1 -> node2;
    };

    indirect -> node1;
}
```

```diff
  while ((*left) && (*left)->left)
-     left = &(AAAA);
+     left = &((*left)->next);
  return *left;
```

### `list_length()`

`list_length()` 也使用間接指標，用來查找整個串列的長度，當中的 left 指標必須向後更新，因此 `BBBB` 為`(*left)->next`

```diff
  while (*left) {
      ++n;
-     left = &(BBBB);
+     left = *((*left)->next);
  }
```


### `quick_sort()`

程式中取第一個元素當成 pivot，因此這邊將 pivot 往後的元素以大小分成左右兩側，將 pivot 向後移，所以 `CCCC` 為 `p->next` 將指標更新

```graphviz
digraph G {
    node [shape = box, fontname = "Times-Roman"];
    rankdir = LR;
    rank = "same";
    
    4 -> 1 -> 3 -> 5 -> 2 -> 7;
    
    pivot [shape = plaintext, fontname = "Times-Roman"];
    pivot;
    
    {
        rank = same;
        pivot -> 4;
    };
}
```

接著把左側放在 i、中間放 pivot、右側放 i + 2，每回合的操作結束就將目前的 index + 2，直到將右側分割完畢，就會從右側依序退回到左側還沒分割的部分並且把已經分割且排序好的元素加回到保存結果的串列中，再繼續重複將剩下的都排序好

```graphviz
digraph G {
    node [shape = box, fontname = "Times-Roman"];
    
    subgraph cluster_1 {
        label = "i";
        1 -> 3 ->2;
    };
    
    subgraph cluster_2 {
        label = "i + 1";
        4;
    };
    
    subgraph cluster_3 {
        label = "i + 2";
        5 -> 7;
    };
    
}
```

```graphviz
digraph G {
    node [shape = box, fontname = "Times-Roman"];
    
    subgraph cluster_1 {
        1 -> 3 -> 2;
    };
    
    subgraph cluster_2 {
        4;
    };
    
    subgraph cluster_3 {
        label = "i";
        NULL;
    };
    
    subgraph cluster_4 {
        label = "i + 1";
        5;
    };
    
    subgraph cluster_5 {
        label = "i + 2";
        7;
    };
}
```

```diff
  while (p) {
      node_t *n = p;
-     p = CCCC;
+     p = p->next;
      list_add(n->value > value ? &right : &left, n);
  }
```

`begin[]` 和 `end[]` 分別放串列的開頭與結尾，所以 `DDDD` 與 `EEEE` 分別為 `list_tail(&left)` 和 `list_tail(&right)`

```diff
  begin[i] = left;
- end[i] = DDDD;
+ end[i] = list_tail(&left);
  begin[i + 1] = pivot;
  end[i + 1] = pivot;
  begin[i + 2] = right;
- end[i + 2] = EEEE;
+ end[i + 2] = list_tail(&right);
```

## 第 1 周測驗題測驗 2

### `merge()`

這裡也使用到間接指標，所以 `AAAA` 為 `&head`

```diff
  struct list_head *head;
- struct list_head **tail = AAAA;
+ struct list_head **tail = &head;
```

比較過後，把適合的節點合併到 `tail` 中，然後將 `tail` 向後移動以添加新的節點，所以 `BBBB` 和 `CCCC` 分別為 `&a->next` 和 `&b->next`

```diff
  if (cmp(priv, a, b) <= 0) {
      *tail = a;
-     tail = BBBB;
+     tail = &a->next;
      a = a->next;
      if (!a) {
          *tail = b;
          break;
      }
  } else {
      *tail = b;
-     tail = CCCC;
+     tail = &b->next;
      b = b->next;
      if (!b) {
          *tail = a;
          break;
      }
  }
```

### `build_prev_link()`

將 `head` 與 `tail` 的關係建立好，形成環狀的雙向佇列

所以 `DDDD` 和 `EEEE` 為 `tail->next` 和 `head->prev`

```diff
- DDDD = head;
- EEEE = tail;
+ tail->next = head;
+ head->prev = tail;
```

### `timsort()`

下面進行最後一次合併，因此當堆疊的大小是小於或等於 1 時，就要將他們的頭尾連接上，形成環狀的佇列

所以 `FFFF` 的值為 1

```diff
  struct list_head *stk0 = tp, *stk1 = stk0->prev;
  while (stk1 && stk1->prev)
      stk0 = stk0->prev, stk1 = stk1->prev;
- if (stk_size <= FFFF) {
+ if (stk_size <= 1) {
      build_prev_link(head, head, stk0);
      return;
  }
```

## 第 2 周測驗題測驗 1

### `hlist_add_head()`

把新加入的節點與原本的開頭連接上，所以 `n->next` 為 `h->first`

```diff
  if (h->first)
      h->first->pprev = &n->next;
- n->next = AAAA;
+ n->next = h->first;
  n->pprev = &h->first;
  h->first = n;
```

### `find()`

找到當前的 hash value 後，在 hash 的此欄位中查找後面所有的節點，所以 `hlist_for_each` 使用的起始為 `&heads[hash]`

為了取得節點的資訊，需要用 `list_entry` 取出

```diff
  struct hlist_node *p;
  int hash = (num < 0 ? -num : num) % size;
- hlist_for_each (p, BBBB) {
-     struct order_node *on = CCCC(p, struct order_node, node);
+ hlist_for_each (p, &heads[hash]) {
+     struct order_node *on = list_entry(p, struct order_node, node);
      if (num == on->val)
          return on->idx;
  }
```

### `node_add()`

找出 hash value 後，要將新節點增加在這個欄位中，所以 `DDDD` 為 `&heads[hash]`

```diff
  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);
+ hlist_add_head(&on->node, &heads[hash]);
```

## 第 2 周測驗題測驗 2

### `hlist_del()`

因為不會有第一個節點的 `pprev` 指向 `NULL` 的狀況，所以可以直接將前一項與後一項相接，因此 `EEEE` 為 `next->pprev`

```diff
  struct hlist_node *next = n->next, **pprev = n->pprev;
  *pprev = next;
  if (next)
-     EEEE = pprev;
+     next->pprev = pprev;
```

### `lRUCacheFree()`

要將 `list_head` 串列中的節點都刪除，`pos` 在 `LRUNode` 結構中的指標為 `link`，而要再將這個連結關係從串列中移除，`GGGG` 為 `&cache->link`

```diff
  struct list_head *pos, *n;
  list_for_each_safe (pos, n, &obj->dhead) {
-     LRUNode *cache = list_entry(pos, LRUNode, FFFF);
-     list_del(GGGG);
+     LRUNode *cache = list_entry(pos, LRUNode, link);
+     list_del(&cache->link);
      free(cache);
  }
  free(obj);
```

### `lRUCacheGet()`

要將 hash 中的某個欄位取出，這裡 `pos` 在 `LRUNode` 中的指標為 `node`，找到後再使用 `list_move` 將整個串列取出，`IIII` 為 `&cache->link`

```diff
  int hash = key % obj->capacity;
  struct hlist_node *pos;
  hlist_for_each (pos, &obj->hhead[hash]) {
-     LRUNode *cache = list_entry(pos, LRUNode, HHHH);
+     LRUNode *cache = list_entry(pos, LRUNode, node);
      if (cache->key == key) {
-         list_move(IIII, &obj->dhead);
+         list_move(&cache->link, &obj->dhead);
          return cache->value;
      }
  }
```

### `lRUCachePut()`

和上面的 `HHHH` 以及 `IIII` 相同，所以 `JJJJ` 為 `node`，`KKKK` 為 `&c->link`

```diff
  LRUNode *cache = NULL;
  int hash = key % obj->capacity;
  struct hlist_node *pos;
  hlist_for_each (pos, &obj->hhead[hash]) {
-     LRUNode *c = list_entry(pos, LRUNode, JJJJ);
+     LRUNode *c = list_entry(pos, LRUNode, node);
      if (c->key == key) {
-         list_move(KKKK, &obj->dhead);
+         list_move(&c->link, &obj->dhead);
          cache = c;
      }
  }
```

## 第 2 周測驗題測驗 3

### `__ffs()`

找出第一個 bit，這邊是應用二分搜尋法，所以 `AAAA` 為 `0xffffffff`

```diff
- if ((word & AAAA) == 0) {
+ if ((word & 0xffffffff) == 0) {
      num += 32;
      word >>= 32;
  }
```

### `__clear_bit()`

先產生一個 mask 在指定 bit 上為 1，最後再將目前的值與 `~mask` 以 and 將此 bit 清除，所以 `BBBB` 為 `~mask`

```diff
  static inline void __clear_bit(unsigned long nr, volatile unsigned long *addr)
  {
      unsigned long mask = BIT_MASK(nr);
      unsigned long *p = ((unsigned long *) addr) + BIT_WORD(nr);

-     *p &= BBBB;
+     *p &= ~mask;
  }
```

### `FIND_NTH_BIT()`

這邊的判斷是為了避免操作到有效位元以外的位數

```diff
- if (sz CCCC BITS_PER_LONG)                              \
+ if (sz % BITS_PER_LONG)                                 \
      tmp = (FETCH) & BITMAP_LAST_WORD_MASK(sz);          \
```