Description

Given the root of a complete binary tree, return the number of the nodes in the tree.

According to Wikipedia, every level, except possibly the last, is completely filled in a complete binary tree, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h.

Design an algorithm that runs in less than O(n) time complexity.

Example 1:

  • Input: root = [1,2,3,4,5,6]
  • Output: 6

Example 2:

  • Input: root = []
  • Output: 0

Example 3:

  • Input: root = [1]
  • Output: 1

Constraints:

  • The number of nodes in the tree is in the range [0, 5 * 104].
  • 0 <= Node.val <= 5 * 104
  • The tree is guaranteed to be complete.

Submitted Code

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution(object):
    def countNodes(self, root):
        """
        :type root: Optional[TreeNode]
        :rtype: int
        """
        if not root:
            return 0

        q = deque()
        q.append(root)
        count = 0

        while q:
            for i in range(len(q)):
                node = q.popleft()
                count += 1

                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
        return count

Runtime: 5 ms | Beats 64.64%
Memory: 28.26 MB | Beats 64.36%

모든 노드를 방문하면서 세는 방법보다 완전 이진 트리라는 것을 활용하는 방법이 있기 때문에 다른 답안들에 비해 속도가 느린 것 같다.

Other Solutions

1st

 class Solution:
        # @param {TreeNode} root
        # @return {integer}
        def countNodes(self, root):
            if not root:
                return 0

            leftDepth = self.getDepth(root.left)        # 왼쪽 깊이
            rightDepth = self.getDepth(root.right)      # 오른쪽 깊이

            if leftDepth == rightDepth:   # 왼쪽 서브트리가 포화 이진 트리
                return pow(2, leftDepth) + self.countNodes(root.right)
            else:                         # 오른쪽 서브트리가 포화 이진 트리
                return pow(2, rightDepth) + self.countNodes(root.left)
        
        def getDepth(self, root):     # 해당 서브트리의 깊이를 구하는 함수(완전 이진 트리이므로 왼쪽만 따라가며 계산)
            if not root:
                return 0
            return 1 + self.getDepth(root.left)

time complexity: 𝑂((log𝑛)2) ← 깊이를 log𝑛번 측정, 측정할 때마다 log𝑛 깊이만큼 내려감
space complexity: 𝑂(log𝑛)

왼쪽과 오른쪽 서브트리를 나누어 깊이를 비교하는 방법이다.
왼쪽 서브트리가 포화 이진 트리라면:
왼쪽 서브트리의 노드 (2leftDepth - 1)개 + 루트 노드 1개 + 오른쪽 서브트리의 노드 개수(재귀 호출)
오른쪽 서브트리가 포화 이진 트리라면:
오른쪽 서브트리의 노드 (2rightDepth - 1)개 + 루트 노드 1개 + 왼쪽 서브트리의 노드 개수(재귀 호출)

root = [1,2,3,4,5,6]

     1
   /   \
  2     3
 / \   /
4   5 6

node 1
leftDepth = 2 (getDepth(2) → 1+getDepth(4) → 1+1+getDepth(None) → 1+1+0)
rightDepth = 2 (getDepth(3) → 1+getDepth(6) → 1+1+getDepth(None) → 1+1+0)
return 2^2 + countNodes(3)

node 3
leftDepth = 1
rightDepth = 0
return 2^0 + countNodes(6)

node 6
leftDepth = 0
rightDepth = 0
return 2^0 + countNodes(None) = 1+0 = 1

4 + (1 + (1 + 0)) = 6

return 6

2nd

class Solution:
    def countNodes(self, root, l=1, r=1):

        if not root : return 0
        
        left = right = root                           # compute both left and right heights of
        while left  := left.left   : l += 1           # each subtree by going all way down to
        while right := right.right : r += 1           # the left and right (in logN time)

        if l == r : return 2**l - 1                   # if it's a full tree, its size is known
        
        return 1 + self.countNodes(root.left) + self.countNodes(root.right)
while left  := left.left   : l += 1
↓
left = left.left
while left:
    l += 1
    left = left.left

Leave a comment