A 是 根節點
。C、D、F、G 是 葉子節點
。A 是 B 和 E 的 父節點
。B 和 E 是 A 的 子節點
。B、E 之間是 兄弟節點
。javascript
高度、深度、層
如上圖所示。前端
爲了方便理解記憶,高度就是擡頭看,深度就是低頭看。
java
與 高度、深度
不一樣,層
類比盜夢空間裏的樓,樓都是從 1 層開始計算,盜夢空間中的樓顛倒過來,從上往下。node
年初立了一個 flag,上面這個倉庫在 2021 年寫滿 100 道前端面試高頻題解,目前進度已經完成了 50%。git
若是你也準備刷或者正在刷 LeetCode,不妨加入前端食堂,一塊兒並肩做戰,刷個痛快。github
瞭解了樹的基礎知識後,立刻開啓咱們愉快的刷題之旅,我整理了 8 道高頻的 LeetCode 鏈表題及題解以下。面試
原題連接數組
中序遍歷:先打印當前節點的左子樹,再打印當前節點,最後打印當前節點的右子樹 (CBDAFEG),如上圖。post
const inorderTraversal = function(root) { const result = []; function pushRoot(root) { if (root !== null) { if (root.left !== null) { pushRoot(root.left); } result.push(root.val); if (root.right !== null) { pushRoot(root.right); } } } pushRoot(root); return result; };
原題連接spa
前序遍歷:先打印當前節點,再打印當前節點的左子樹,最後打印當前節點的右子樹 (ABCDEFG),如上圖。
const preorderTraversal = function(root) { const result = []; function pushRoot(node){ if (node !== null) { result.push(node.val); if (node.left !== null){ pushRoot(node.left); } if (node.right !== null) { pushRoot(node.right); } } } pushRoot(root); return result; };
後序遍歷:先打印當前節點的左子樹,再打印當前節點的右子樹,最後打印當前節點 (CDBFGEA),如上圖。
const postorderTraversal = function(root) { const result = []; function pushRoot(node) { if (node !== null) { if (node.left !== null) { pushRoot(node.left); } if (node.right !== null) { pushRoot(node.right); } result.push(node.val); } } pushRoot(root); return result; };
const isSameTree = function(p, q) { if (p === null && q === null) return true; if (p === null || q === null) return false; if (p.val !== q.val) return false; return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); };
先明確,所謂「對稱」,也就是兩個樹的根節點相同
且
const isSymmetric = function(root) { if (root === null) return true return isEqual(root.left, root.right) // 比較左右子樹是否對稱 }; const isEqual = function(left, right) { // 遞歸終止條件 if (left === null && right === null) return true // 對稱 if (left === null || right === null) return false // 不對稱 // 比較左右子樹的 root 值以及左右子樹是否對稱 return left.val === right.val && isEqual(left.left, right.right) && isEqual(left.right, right.left) }
按照樹的深度將每層對應的節點添加到對應層的數組中便可。
const levelOrder = function(root) { if (!root) return [] const res = [] dfs(root, 0, res) return res }; const dfs = function(root, depth, res) { if (!root) return // 遞歸終止條件 if (!res[depth]) res[depth] = [] res[depth].push(root.val) // 存入每層的節點值 dfs(root.left, depth + 1, res) // drill down dfs(root.right, depth + 1, res) }
根據層次返回其對應的結果集合。
const levelOrder = function(root) { if (!root) return [] const queue = [root] const res = [] while (queue.length > 0) { const arr = [] let len = queue.length while (len) { let node = queue.shift() arr.push(node.val) if (node.left) queue.push(node.left) if (node.right) queue.push(node.right) len-- } res.push(arr) } return res };
樹的深度 = 左右子樹的最大深度 + 1
const maxDepth = function(root) { if (!root) { // 遞歸終止條件 return 0 } else { const left = maxDepth(root.left) const right = maxDepth(root.right) return Math.max(left, right) + 1 } };
層序遍歷時記錄樹的深度。
二叉樹的層序遍歷可參考輕鬆拿下二叉樹的層序遍歷
const maxDepth = function(root) { let depth = 0 if (root === null) { return depth } const queue = [root] while (queue.length) { let len = queue.length while (len--) { const cur = queue.shift() cur.left && queue.push(cur.left) cur.right && queue.push(cur.right) } depth++ } return depth };
Google:咱們 90% 的工程師都用你寫的軟件(Homebrew),但你無法在白板上翻轉二叉樹,因此翻滾吧,蛋炒飯。
原推截圖,至今仍在。 Max Howell 當年吐槽以後 LeetCode 立刻加入了這道題。
會了這道題,是否是咱們也能夠超越世界級大牛了?(狗頭保命)
首先明確,所謂二叉樹的翻轉須要知足如下兩點:
const invertTree = function(root) { if (root === null) return null // 遞歸終止條件 invertTree(root.left) invertTree(root.right) const temp = root.left root.left = root.right root.right = temp return root }
固然你也能夠將上面的 2,3 兩個步驟顛倒執行,也就是先交換兩棵子樹的位置,再對其內部進行翻轉。
const invertTree = function(root) { if (root === null) return null // 遞歸終止條件 const temp = root.left root.left = root.right root.right = temp invertTree(root.left) invertTree(root.right) return root }
層序遍歷遍歷二叉樹,當根結點出列時,翻轉它的左右子樹。而後將其左右子節點入列,以便下一層時翻轉。
二叉樹的層序遍歷可參考輕鬆拿下二叉樹的層序遍歷
const invertTree = (root) => { if (root == null) return null; const queue = [root]; while (queue.length) { const cur = queue.shift(); [cur.left, cur.right] = [cur.right, cur.left]; if (cur.left) queue.push(cur.left); if (cur.right) queue.push(cur.right); } return root; }