# 2020q3 Homework7 (quiz7)
contributed by < `ccs100203` >
###### tags: `linux2020`
> [第 7 週測驗題](https://hackmd.io/@sysprog/2020-quiz7)
## 測驗 1
選出何者為 [tail recursion](https://en.wikipedia.org/wiki/Tail_call)
```cpp
int funcA(int i)
{
if (i == 0)
return 0;
return 5 * funcA(i - 1);
}
int funcB(int i)
{
if (i == 0)
return 0;
return funcB(i - 1);
}
```
Tail recursion 的好處在於程式不需要完全保留上一層 stack 的資訊,可以把他視為一個 for loop 使用,如此一來 stack 的增長就會比原本的 recursion 方式還慢,更省空間也更不容易 stackoverflow。
- funA
不是 Tail recursion,原因在 `return 5 * funcA(i - 1);`,由於這邊多做了乘法運算,導致程式必須保留住上一層 stack 的資訊,為了讓程式在 `return` 回來後能夠做 `5 * funcA` 的運算,以至於無法提早從 stack pop 掉。
- funB
是 Tail recursion,在 `return funcB(i - 1);` 時,程式會在當前的 stack 得到想要的運算結果才進入下一層 stack,程式並不需要在 `return` 回來後額外做運算,代表之前的 stack 的內容除了 return address 外不再會用到,所以對此程式可以做優化,提早 pop 掉之前 stack 的內容以節省空間。
## 測驗 2
考慮測試 C 編譯器 [Tail Call Optimization (TCO)](https://en.wikipedia.org/wiki/Tail_call) 能力的程式 [tco-test](https://github.com/sysprog21/tco-test)
從實驗中可發現下方程式無法對 g 函式施加 TCO:
```cpp
void g(int *p);
void f(void) {
int x = 3;
g(&x);
}
void g(int *p) { printf("%d\n", *p); }
```
因為函式 f 的區域變數 x 在返回後就不再存在於 stack。考慮以下程式碼:
```cpp=
int *global_var;
void f(void)
{
int x = 3;
global_var = &x;
...
/* Can the compiler perform TCO here? */
g();
}
```
下方的程式碼,如果 `g` 沒有對 `global_var` 指標作 `dereference`,那麼就可以提前從 stack pop 掉 `f` 內的資料。反之,如果需要用 `global_var` 代表他需要讀取 `x`,如果提前把 `f` 內的資料 pop 掉就會讀取不到而出現問題。
## 測驗 3
[LeetCode 226. Invert Binary Tree](https://leetcode.com/problems/invert-binary-tree/)
題目要對 binary tree 做 mirror
![](https://i.imgur.com/ytvcJRl.png)
```cpp=
struct TreeNode *invertTree(struct TreeNode *root)
{
if (!root)
return NULL;
if (!root->left && !root->right)
return root;
struct TreeNode *tmp = root->left;
root->left = root->right;
root->right = tmp;
invertTree(root->left);
invertTree(root->right);
return root;
}
```
程式的原理就是對子節點做左右交換,一直到每一個 node 都被交換過就會停止。
所以先換左子樹或右子樹對程式並不會有任何影響。
給定
![](https://i.imgur.com/NMvHEeI.png =500x)
最一開始會先交換 2 跟 3,此時程式執行到第 11 行,會發現左半邊跟右半邊交換了。
![](https://i.imgur.com/rlnLP4h.png =500x)
再來看到第 12~13 行,這時候只是選擇要從左邊 (紅色框框) 還是右邊 (綠色框框) 先執行而已,所以兩者的順序並不會造成影響。
![](https://i.imgur.com/I3VAzzc.png =500x)
## 測驗 4
[LeetCoee 201. Bitwise AND of Numbers Range](https://leetcode.com/problems/bitwise-and-of-numbers-range/)
Given a range [m, n] where 0 <= m <= n <= 2147483647, return the bitwise AND of all numbers in this range, inclusive.
給定兩個正整數,將 m 跟 n 之間的所有數字做 AND 運算。
```cpp=
int rangeBitwiseAnd(int m, int n)
{
if (m == 0) return 0;
int diff = n - m;
int count = diff ? 32U - __builtin_clz(diff) : 0;
return m & n & ~((1U << count) - 1);
}
```
利用 `n` 跟 `m` 的差距 `diff`,可以知道哪些 bit 是不需要考慮的。因為從 $m+1, m+2...m+diff$,在運算這些數字時,`diff` 範圍內的每一個 bit 一定都會在 0 跟 1 之間轉換。那麼在 `AND` 過後這些全部會變成 0,所以不需要考慮。
舉例來說 6~9,`32U - __builtin_clz(diff)` 等於 2,代表最小的兩個 bit 不須考慮,因為在運算過程中這些 bit 一定會在某個數字變成 0。
| | 3 | 2 | 1 | 0 |
| :-: | :-: | :-: | :-: | :-: |
| 6 | 0 | 1 | 1 | 0 |
| 7 | 0 | 1 | 1 | 1 |
| 8 | 1 | 0 | 0 | 0 |
| 9 | 1 | 0 | 0 | 1 |
|diff| 0 | 0 | 1 | 1 |
了解原理後要來設計 mask,假設 $n - m = k, k > 0$,藉由 `32U - __builtin_clz(k)` 得到 `k` 總共有多少個位數 `count`,也可以等價於 $\lfloor log_{2}{k} \rfloor +1$。代表從 LSB 開始有 `count` 個 bit 需要被設計成 0,其他為 1。mask 就可以設計成 $\underbrace{11...11}_\text{32 - count}\underbrace{0...00}_\text{count}$ 的樣子。
假設 `count` 為 2
| | 3 | 2 | 1 | 0 |
| :-: | :-: | :-: | :-: | :-: |
| `count` | 0 | 0 | 1 | 0 |
| `1U << count` | 0 | 1 | 0 | 0 |
| `(1U << count)-1` | 0 | 0 | 1 | 1 |
|`~((1U << count)-1)`<br>**mask**| 1 | 1 | 0 | 0 |
## 測驗 5
[LeetCode 97. Interleaving String](https://leetcode.com/problems/interleaving-string/)
![](https://i.imgur.com/1VvBJiZ.png)
判斷給定的字串 s1 與 s2 可否交錯形成字串 s3。從上圖可見,走訪 s3 所有字元,其字元必定是來自 s1 或來自 s2,並且 s1 與 s2 的字元也是依序取出,也就是 s3[i3] 必定為 s1[i1] 或 s2[i2],其中 i1, i2, i3 分別為 s1, s2, s3 目前的位置。
```cpp=
bool isInterleave(char *s1, char *s2, char *s3)
{
size_t n = strlen(s1), m = strlen(s2);
if ((n + m) != strlen(s3))
return false;
bool dp[m + 1];
memset(dp + 1, false, m * sizeof(bool));
dp[0] = true;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
if (i == 0 && j == 0)
continue;
int p = i + j - 1;
if (i == 0)
dp[j] = dp[j - 1] & (s2[j - 1] == s3[p]);
else {
dp[j] &= (s1[i - 1] == s3[p]);
if (j > 0)
dp[j] |= dp[j - 1] & (s2[j - 1] == s3[p]);
}
}
}
return dp[m];
}
```
TODO