觀感度:🌟🌟🌟🌟🌟前端
口味:東北小炒肉vue
烹飪時間:10mingit
本文已收錄在前端食堂同名倉庫Github github.com/Geekhyt,歡迎光臨食堂,若是以爲酒菜還算可口,賞個 Star 對食堂老闆來講是莫大的鼓勵。
& ,與
兩個位都爲 1 時,結果才爲 1| ,或
兩個位都爲 0 時,結果才爲 0^ ,異或
兩個位相同爲 0 ,相異爲 1~,按位取反
全部 0 變 1,1 變 0<<,左移
各二進位所有左移若干位,高位丟棄,低位補 0>>,右移
各二進位所有右移若干位,對無符號數,高位補 0 ,有符號數,各編譯器處理方法不同,有的補符號位,有的補 0其實很簡單,小學數學題難度,花幾分鐘看完若是看懂了請點個讚唄。github
二進制左移一位,就是將數字翻倍。
面試
二進制 110101
向左移一位,就是在末尾添加一位 0,也就是 1101010
。(此處討論的是數字沒有溢出的狀況)算法
二進制 110101 轉化成十進制:數組
1 2^5 + 1 2^4 + 0 2^3 + 1 2^2 + 0 2^1 + 1 2^0= 32 + 16 + 0 + 4 + 0 + 1 數據結構
= 53函數
二進制 1101010 轉化成十進制:post
1 2^6 + 1 2^5 + 0 2^4 + 1 2^3 + 0 2^2 + 1 2^1 + 0 * 2^0= 64 + 32 + 0 + 8 + 0 + 2 + 0
= 106
二進制右移一位,就是將數字除以 2 並求整數商。
二進制 110101
向右移一位,就是去除掉末尾的那一位,也就是 11010
。
二進制 11010 轉化成十進制:
1 2^4 + 1 2^3 + 0 2^2 + 1 2^1 + 0 * 2^0= 16 + 8 + 0 + 2 + 0
= 26
無符號右移使用 >>>
表示,而有符號右移使用 >>
表示。
>>>
無符號右移 1 位,右邊丟棄,左邊補 0 便可。
>>
有符號右移保留符號,拷貝最左側的位來填充左側,向右位移並丟棄最右邊的位。
因爲左移位無需考慮高位補 1 仍是補 0(符號位可能爲 1 或 0),因此不須要區分無符號左移和有符號左移。
參與操做的位中只要有一個位是 1, 那麼最終結果就是 1。
若是咱們將 110101
和 100011
進行按位的或操做,就會獲得 110111
。
參與操做的位中必須都是 1,最終結果纔是 1,不然爲 0。
若是咱們將 110101
和 100011
進行按位的與操做,就會獲得 100001
。
參與操做的位相同,最終結果是 0 ,不然爲 1。
想要獲得 1,參與操做的兩個位必須不相同,也就是異或中「異」的含義。
若是咱們將 110101
和 100011
進行按位的異或操做,就會獲得 10110
。
x % 2 === 1 -> (x & 1) === 1 (奇數)
x % 2 === 0 -> (x & 1) === 0 (偶數)
即:x = x / 2; -> x = x >> 1;
mid = (left + right) / 2; -> mid = (left + right) >> 1;
清零最低位的 1,表明將最後一位 1 變成 0。
獲得最低位的 1,表明除最後一位 1 保留,其餘位所有爲 0。
x & (~0 << n)
(x >> n) & 1
x & (1 << (n - 1))
x | (1 << n)
x & (~(1 << n))
x & ((1 << n) - 1)
x & (~((1 << (n + 1)) - 1))
n 皇后問題研究的是如何將 n 個皇后放置在 n×n 的棋盤上,而且使皇后彼此之間不能相互攻擊。
(圖片來源LeeCode,同上原題連接)
上圖爲 8 皇后問題的一種解法。給定一個整數 n,返回 n 皇后不一樣的解決方案的數量。
示例:
輸入: 4 輸出: 2 解釋: 4 皇后問題存在以下兩個不一樣的解法。 [ [".Q..", // 解法 1 "...Q", "Q...", "..Q."], ["..Q.", // 解法 2 "Q...", "...Q", ".Q.."] ]
提示:
皇后能夠橫、直、斜走,格數不限。題目要求皇后彼此之間不能相互攻擊,也就是說須要知足任意兩個皇后不能在同一行、同一列以及同一條斜線上。
熟悉這道題的同窗,能夠看出最直觀的作法是利用回溯法進行求解。
遍歷枚舉出全部可能的選擇,依次在每一行放置一個皇后,每次新放置的皇后不能和已經放置的皇后之間存在攻擊。
爲了下降時間複雜度,最理想的狀況是在 O(1) 的時間內判斷該位置所在的幾條線上是否已經有皇后,能夠利用集合來進行位置判斷。
爲了讓你更好的理解,我利用回溯法將 4 皇后可能的解法畫了出來。以下圖所示:
分治:分而治之,先解決子問題,再將子問題的解合併求出原問題。
貪心:一條路走到黑,選擇當下局部最優的路線,沒有後悔藥。
回溯:一條路走到黑,手握後悔藥,能夠無數次重來。(英雄聯盟艾克大招無冷卻)。
動態規劃:上帝視角,手握無數平行宇宙的歷史存檔,同時發展出無數個將來。
下面這張圖是兩條對角線方向的斜線的規律,聰明的你確定一眼就能看出來:
以下所示是官方給出的題解:
const backtrack = (n, row, columns, diagonals1, diagonals2) => { if (row === n) { return 1; } else { let count = 0; for (let i = 0; i < n; i++) { if (columns.has(i)) { continue; } const diagonal1 = row - i; if (diagonals1.has(diagonal1)) { continue; } const diagonal2 = row + i; if (diagonals2.has(diagonal2)) { continue; } columns.add(i); diagonals1.add(diagonal1); diagonals2.add(diagonal2); count += backtrack(n, row + 1, columns, diagonals1, diagonals2); columns.delete(i); diagonals1.delete(diagonal1); diagonals2.delete(diagonal2); } return count; } } const totalNQueens = function(n) { const columns = new Set(); const diagonals1 = new Set(); const diagonals2 = new Set(); return backtrack(n, 0, columns, diagonals1, diagonals2); };
學會了位運算,你能夠將代碼寫的更加優雅。
先來明確幾個概念和須要用到的公式:
n:n層
row:當前層
cols:列
pie:撇,左斜線(副對角線)
na:捺,右斜線(正對角線)
二進制爲 1,表明不可放置,0 相反
x & -x :獲得最低位的1 (表明除最後一位 1 保留,其餘位所有爲 0)
x & (x - 1):清零最低位的 1 (表明將最後一位 1 變成 0)
x & ((1 << n) - 1):將 x 的最高位至第 n 位(含)清零
將 N 個位置對應成 N 個二進制位,0 表明能夠選擇,1 表明不能選擇。好比八皇后當前第一行的第二位被選擇時的狀態是 00100000,那麼下一行的第二位也不能被選擇,正對角線(na)對應的第三位不能被選擇(對應當前行右移了一位),狀態表示爲:00100000。副對角線(pie)對應的第一位不能被選擇(對應當前行左移了一位),狀態表示爲 10000000。
const totalNQueens = function(n) { let res = 0; const dfs = (n, row, cols, pie, na) => { if (row >= n) { res++; return; } let bits = (~(cols | pie | na)) & ((1 << n) - 1) // 1 while (bits) { // 2 let p = bits & -bits // 3 dfs(n, row + 1, cols | p, (pie | p) << 1, (na | p) >> 1) // 4 bits = bits & (bits - 1) // 5 } } dfs(n, 0, 0, 0, 0); return res; };
1.cols | pie | na
表示全部可以被皇后攻擊的格子,~(cols | pie | na)
取反表示將沒有被佔的格子從 0 變爲 1,以便後續的位遍歷。這裏用到公式:x & ((1 << n) - 1):將 x 的最高位至第 n 位(含)清零。
一個 int 的二進制位至少有 32 位,咱們將前面不須要的位置清零。因此,這行代碼表示獲得當前全部的空位,也就是能夠放置皇后的格子。
2.只要 bits 中有 1,就說明還有格子能夠放置皇后,每次遍歷都會將其清零(表示在p位置放入了皇后),也就是註釋 5 的代碼含義。對應公式:x & (x - 1):清零最低位的 1 (表明將最後一位 1 變成 0)。
3.對應公式:x & -x :獲得最低位的1 (表明除最後一位 1 保留,其餘位所有爲 0)
,表示當前皇后可放入的位置。
4.修改狀態,進入下一層遞歸。row + 1 表明搜索下一行,cols | p 表明目前全部能夠放置皇后的列。(pie | p) << 1
,(na | p) >> 1
,在上面思路中已經說過了,再也不贅述。
Vue3 中也有一些關於位運算的實踐。
shapeFlags 針對 VNode 的 type 進行了更詳細的分類,便於在 patch 階段,根據不一樣的類型執行相應的邏輯。
// packages/shared/src/shapeFlags.ts export const enum ShapeFlags { ELEMENT = 1, // HTML 或 SVG 標籤 普通 DOM 元素 FUNCTIONAL_COMPONENT = 1 << 1, // 函數式組件 STATEFUL_COMPONENT = 1 << 2, // 普通有狀態組件 TEXT_CHILDREN = 1 << 3, // 子節點是純文本 ARRAY_CHILDREN = 1 << 4, // 子節點是數組 SLOTS_CHILDREN = 1 << 5, // 子節點是插槽 TELEPORT = 1 << 6, // Teleport SUSPENSE = 1 << 7, // Suspense COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 須要被 keep-alive 的有狀態組件 COMPONENT_KEPT_ALIVE = 1 << 9, // 已經被 keep-alive 的有狀態組件 COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT // 有狀態組件和函數組件都是組件,用 COMPONENT 表示 }
patchFlags 用於標識節點更新的類型,用於運行時優化。
// packages/shared/src/patchFlags.ts export const enum PatchFlags { TEXT = 1, // 動態文本節點 CLASS = 1 << 1, // 動態 class STYLE = 1 << 2, // 動態 style PROPS = 1 << 3, // 動態屬性 FULL_PROPS = 1 << 4, // 具備動態 key 屬性,當 key 改變時,須要進行完整的 diff 比較 HYDRATE_EVENTS = 1 << 5, // 具備監聽事件的節點 STABLE_FRAGMENT = 1 << 6, // 子節點順序不會被改變的 fragment KEYED_FRAGMENT = 1 << 7, // 帶有 key 屬或部分子節點有 key 的 fragment UNKEYED_FRAGMENT = 1 << 8, // 子節點沒有 key 的 fragment NEED_PATCH = 1 << 9, // 非 props 的比較,好比 ref 或指令 DYNAMIC_SLOTS = 1 << 10, // 動態插槽 DEV_ROOT_FRAGMENT = 1 << 11, // 僅供開發時使用,表示將註釋放在模板根級別的片斷 HOISTED = -1, // 靜態節點 BAIL = -2 // diff 算法要退出優化模式 }
經過進行 | 或運算進行標記的組合,若是當前節點是一個動態文本節點(0000 0001),它同時又具備動態 style (0000 0100),兩者進行 | 或運算後值爲 (0000 0101)。
經過進行 & 與運算進行標記的檢查。能夠點擊此處跳轉到源碼倉庫進行查看
讀這部分註釋的時候發現了引用文件路徑的錯誤,提交了Pr,成功混入了 Vue Contributor
,與尤大進行了一波親密互動。
因此說,好好學習是有回報的,一塊兒加油吧,打工人!
若是你對 Vue3 DOM Diff
核心算法感興趣的話,也歡迎閱讀個人另一篇專欄,Vue3 DOM Diff 核心算法解析
更有其餘算法系列專欄,讓你一次看過癮:
1.若是你以爲食堂酒菜還合胃口,就點個贊支持下吧,你的贊是我最大的動力。
2.關注公衆號前端食堂,吃好每一頓飯!
3.點贊、評論、轉發 === 催更!