寫這篇文章主要是記錄一下本身面試被問到的這個問題: 求二叉樹每層最大值 到 構造一顆二叉樹去驗證 到進一步優化循環層數 的解答過程。(面試常見套路,手寫->優化/其餘解法/時間空間複雜度分析)面試
時間一久就會忘記怎麼寫,下次又得想半天,而面試確定不會讓現場想半天的,因此須要熟記思路。算法
這個題目源於我寫的字節跳動實習的面經 http://www.javashuo.com/article/p-nmqxvowz-nz.html,將解答過程寫進面經裏面會比較長,因此單獨抽出來總結一下。segmentfault
這個方法比較常見,記得數據結構書中二叉樹層序遍歷的代碼示例就是這個方法,leetcode的題解也可能是兩層循環。 數據結構
ps:若是一開始就寫出一層循環那固然不會有後續讓優化的問題了。但推薦寫這種,後續大多會被問到怎麼用一層實現(我小米和字節都被問到了)。這時再裝做想想而後寫出來豈不是更顯得本身應變能力強,有算法基礎而不是背出來的?函數
var largestValues = function(root) { if(!root) return [] const res = [] // 最終輸出的結果,存放每層的最大值 let queue = [root] // 根節點入隊 while(queue.length) { let len = queue.length let tmp = [] // 存放當前層的全部節點的值 while(len) { // 此處的len記錄的是當前層的節點數,爲0表明遍歷完當前層的節點,開始遍歷下一層 let curr = queue.shift() if(curr.left) queue.push(curr.left) if(curr.right) queue.push(curr.right) tmp.push(curr.val) len-- } res.push(Math.max(...tmp)) } return res }
// 二叉樹的節點的構造方法 function TreeNode(val, left, right) { this.val = val this.left = null this.right = null } // 接下來咱們構建一顆第一層爲1,第二層爲2,3,第三層爲4,5,6的二叉樹 let p1 = new TreeNode(1) let p2 = new TreeNode(2) let p3 = new TreeNode(3) let p4 = new TreeNode(4) let p5 = new TreeNode(5) let p6 = new TreeNode(6) // 將這些節點相互鏈接起來 p1.left = p2 p1.right = p3 p2.left = p4 p2.right = p5 p3.left = p6 // 驗證 console.log(largestValues(p1)); // 輸出 [1, 3, 6]
思路:在前面的方法中,咱們內層循環的做用是記錄當前層的節點數,讓咱們知道何時本層節點遍歷完。既然少了一層循環,那咱們就只能用額外空間去記錄下一層節點了優化
var largestValuesImprove = function(root) { if(!root) return [] const res = [] // 最終輸出的結果,存放每層的最大值 let queue = [root] // 存放當前層的節點 let nextLevel = [] // 存放下一層的節點 let tmp = [] while(queue.length>0 || nextLevel.length>0) { if(queue.length===0) { // 當前層遍歷完 queue = nextLevel // 取下一層 nextLevel = [] res.push(Math.max(...tmp)) tmp = [] } let curr = queue.shift() tmp.push(curr.val) if(curr.left) nextLevel.push(curr.left) if(curr.right) nextLevel.push(curr.right) } // 這裏必定要注意最後一次跳出循環時沒有走if語句,須要再把最後一層的值放入res res.push(Math.max(...tmp)) return res } // 驗證 console.log(largestValuesImprove(p1));
完整代碼this
// 1. 求二叉樹每層的最大節點 var largestValues = function(root) { if(!root) return [] const res = [] // 最終輸出的結果,存放每層的最大值 let queue = [root] // 根節點入隊 while(queue.length) { let len = queue.length let tmp = [] // 存放當前層的全部節點的值 while(len) { // 此處的len記錄的是當前層的節點數,爲0表明遍歷完當前層的節點,開始遍歷下一層 let curr = queue.shift() if(curr.left) queue.push(curr.left) if(curr.right) queue.push(curr.right) tmp.push(curr.val) len-- } res.push(Math.max(...tmp)) } return res } // 2. 構造一顆二叉樹,用於驗證咱們寫的求每層最大值的函數 function TreeNode(val, left, right) { // 節點的構造方法 this.val = val this.left = null this.right = null } // 接下來咱們構建一顆第一層爲1,第二層爲2,3,第三層爲4,5,6的二叉樹 let p1 = new TreeNode(1) let p2 = new TreeNode(2) let p3 = new TreeNode(3) let p4 = new TreeNode(4) let p5 = new TreeNode(5) let p6 = new TreeNode(6) // 將這些節點相互鏈接起來 p1.left = p2 p1.right = p3 p2.left = p4 p2.right = p5 p3.left = p6 // 3. 驗證 console.log(largestValues(p1)); // 輸出 [1, 3, 6] // 4. 循環層數優化:將咱們剛剛寫求二叉樹每層的最大值中兩層循環改成一層 // 思路:在前面的方法中,咱們內層循環的做用是記錄當前層的節點數,讓咱們知道何時本層節點遍歷完。 // 既然少了一層循環,那咱們就只能用額外空間去記錄下一層節點了 var largestValuesImprove = function(root) { if(!root) return [] const res = [] // 最終輸出的結果,存放每層的最大值 let queue = [root] // 存放當前層的節點 let nextLevel = [] // 存放下一層的節點 let tmp = [] while(queue.length>0 || nextLevel.length>0) { if(queue.length===0) { // 當前層遍歷完 queue = nextLevel // 取下一層 nextLevel = [] res.push(Math.max(...tmp)) tmp = [] } let curr = queue.shift() tmp.push(curr.val) if(curr.left) nextLevel.push(curr.left) if(curr.right) nextLevel.push(curr.right) } // 這裏必定要注意最後一次跳出循環時沒有走if語句,須要再把最後一層的值放入res res.push(Math.max(...tmp)) return res } console.log(largestValuesImprove(p1));