contributed by < ccs100203
>
linux2020
TODO 延伸問題 1、2、3、4
LeetCode 461. Hamming Distance
__builtin_popcount
計算總共有多少 1 就可以知道答案。diff | A | B |
---|---|---|
0 | 0 | 0 |
1 | 0 | 1 |
1 | 1 | 0 |
0 | 1 | 1 |
題目會給定一條 array,算出所有數字之間 hamming distance
最直覺的做法就是暴力解,對每個數字做 __builtin_popcount(x ^ y)
,但這毫不易外的 TLE。
所以我採取的方法是,比較每一組 bit,每挑出一對 0 跟 1,hamming distance 就會 +1,所以得出以下結論,我只要找出每組 bit 中有多少 0 跟 1,運用組合的方式挑出所有答案。
LeetCode 1483. Kth Ancestor of a Tree Node
給你一棵樹,樹上有 n 個節點,編號自 0 到 n−1。樹以父節點陣列的形式提供,其中 parent[i] 是節點 i 的父節點。樹的根節點是編號為 0 的節點。請設計 treeAncestorGetKthAncestor(TreeAncestor *obj, int node, int k) 函式,回傳節點 node 的第 k 個祖先節點。若不存在這樣的祖先節點,回傳 -1。樹節點的第 k 個祖先節點是從該節點到根節點路徑上的第 k 個節點
treeAncestorCreate
會建立出一個 n * max_level 的 TreeAncestor array,此 array 的用途為記錄 node 的 parent,比較特別的是這邊使用 的方式去記錄,假設 n = 7,那只會記錄 3 個 parent,分別是第 、、 的 parent,其他的 parent 只需要利用 1、2、4 這三個值的組合就可以得到。這也是 max_level 的用處,就是存放 ,代表 array 需要多少個 column。
int max_level = 32 - __builtin_clz(n) + 1;
實際上 max_level
宣告的時候是 ,最後的 + 1 我認為是浪費空間,他的存在原因為下方第 22 行的 for 並沒有寫終止條件。之後會對這個部分做修正。quit
就是用來當第 22 行 for 迴圈的終止條件,當有一個 column 全部為 -1 時就會 break
。treeAncestorGetKthAncestor
這邊就是去做查表,利用 if (k & (1<<i))
去做到查詢的作用。
假設 k = 5,二進位為 0101,利用 + 去組合出 5。那麼答案就會是
parent[ parent[node][1 << 0] ][1 << 2]
在這邊遇到一個問題,繳交同一份程式碼會有各種不同的執行時間。
考慮貌似簡單卻蘊含實作深度的 FizzBuzz 題目:
先從這邊來看,此題的關鍵在於如何精準的控制 start
以及 length
,藉由他們去印出答案。
以下為完整程式碼:
unsigned int length = (2 << div3) << div5;
若能整除 3 或 5 則為 4,兩者同時符合則為 8,剩下就是印數字本身所以長度為 2。is_divisible
可以參考 2020q3 Homework2 (quiz2) 的解釋&"FizzBuzz%u"[ (MSG_LEN >> KK1) >> (KK2 << KK3) ]
括號內的動作就是在判定 start
的位置,用來當 array 的 index。start
的可能性:div3 | div5 | start |
---|---|---|
0 | 0 | 8 |
1 | 0 | 0 |
0 | 1 | 4 |
1 | 1 | 0 |
div3
為 1, start
就應為 0,所以可判斷出 (MSG_LEN >> KK1) >> (div3 << 2)
。再來藉由 (MSG_LEN >> div5)
去處理剩下的情況也就不難想到。考慮到整數 PAGE_QUEUES 可能有以下數值:
在整數除法時,可思考加速運算的機制
將原本的運算轉換為下列形式
PAGE_QUEUES
範圍,所以在 5~6 行先做 shift 就是為了降低除法的運算成本。可以藉由 ffs32(x)
得到 divident
有多少 tail zero,將這些 / 2
的部分用 shift 取代掉。divident
只會剩下 1、3、5、7 四種數值,所以最後的 CASES
就必須包含 3、5、7。__builtin_ffs