1. 程式人生 > >完全二叉樹節點個數

完全二叉樹節點個數

文章目錄

完全二叉樹節點個數

最簡單的解法就是遍歷這棵樹,時間複雜度是O(N),如果只是這一種解法,就不會有本文了。本文中給出兩種解法。

完全二叉樹

第一次看到完全二叉樹的定義比較懵,搜尋到的定義如下:

設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第h層所有的結點都連續集中在最左邊,這就是完全二叉樹。

誰到不想看這個定義,比較簡單的理解就是和滿二叉樹相比較,看按層次遍歷的標號是否一致。具體方案不再闡述,網上都有。

實現

  • 遍歷
  • 藉助完全二叉樹的特點
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */

遍歷

看一下遞迴的公式:

  • 空樹:返回0
  • 非空樹:遞迴的統計左右子樹的節點個數,返回左右子樹節點個數加1的和
func countNodes(root *TreeNode) int {
    if root == nil {
        return 0
    }
    
    sumL := countNodes(root.Left)
    sumR := countNodes(root.Right)
    
    return sumL+sumR+1
}

藉助完全二叉樹的特點

這個特點是什麼呢?完全二叉樹=滿二叉樹+完全二叉樹。完全二叉樹裡藏著一顆滿二叉樹和另外一顆完全二叉樹,最驚喜的是從公式裡看出子問題和原問題基本一致,可以使用遞迴啊。

完全二叉樹的層數

這種演算法的實現需要知道完全二叉樹的層數,難道又需要遍歷統計嗎,當然不需要了,還是藉助完全二叉樹的特點,可以根據完全二叉樹的最左節點和一個計數變數搞定,因為這個最左節點肯定是位於最後一層。

func height(root *TreeNode) int {
    h := 0
    
    for root != nil {
        h++
        root = root.Left
    }
    
    return h
}

原理

  • h = height(root), hright = height(root.Right)
  • 左子樹和右子樹其中有一顆是完全二叉樹,另一顆是滿二叉樹
  • 區別左右子樹的種類根據h-hright
    • h-hright=1:
      • 左子樹:滿二叉樹
      • 右子樹:完全二叉樹
    • h-hright=2:
      • 左子樹:完全二叉樹
      • 右子樹:滿二叉樹
  • 節點個數=1+左子樹節點個數+右子樹節點個數 = (1 << hright)+遞迴另一顆完全二叉樹(滿二叉樹 = 1 << hright)

第一種情況

image

第二種情況

image

實現

func countNodes(root *TreeNode) int {
    if root == nil {
        return 0
    }
    
    return nodeNums(root)
}

func nodeNums(root *TreeNode) int {
    h := height(root)
    hR := height(root.Right)
    
    //兩種情況滿二叉樹層數都是hR
    //不同在於遞迴左子樹還是右子樹
    nums := 1 << uint(hR)
    if hR+1 == h {
        return nums+countNodes(root.Right)
    }
    
    return nums+countNodes(root.Left)
}

func height(root *TreeNode) int {
    h := 0
    
    for root != nil {
        h++
        root = root.Left
    }
    
    return h
}

時間複雜度

對於每一個節點最多需要訪問深度個數的節點,所以最終時間複雜度是O(lg2*lg2)。

關於作者

大四學生一枚,分析資料結構,面試題,golang,C語言等知識。QQ交流群:521625004。微信公眾號:後臺技術棧。
image