記一次面試被問 【求二叉樹每層最大值->構造二叉樹驗證->優化循環層數】 的解答過程

背景

寫這篇文章主要是記錄一下本身面試被問到的這個問題: 求二叉樹每層最大值 到 構造一顆二叉樹去驗證 到進一步優化循環層數 的解答過程。(面試常見套路,手寫->優化/其餘解法/時間空間複雜度分析)面試

時間一久就會忘記怎麼寫,下次又得想半天,而面試確定不會讓現場想半天的,因此須要熟記思路。算法

這個題目源於我寫的字節跳動實習的面經 http://www.javashuo.com/article/p-nmqxvowz-nz.html,將解答過程寫進面經裏面會比較長,因此單獨抽出來總結一下。segmentfault


1. 求二叉樹每層的最大節點

這個方法比較常見,記得數據結構書中二叉樹層序遍歷的代碼示例就是這個方法,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
}

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

  // 驗證
  console.log(largestValues(p1));   // 輸出 [1, 3, 6]

3. 循環層數優化:將剛剛寫的求二叉樹每層最大值的方法中的兩層循環改成一層

思路:在前面的方法中,咱們內層循環的做用是記錄當前層的節點數,讓咱們知道何時本層節點遍歷完。既然少了一層循環,那咱們就只能用額外空間去記錄下一層節點了優化

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));
相關文章
相關標籤/搜索