class Solution:
    def isValidSerialization(self, preorder: str) -> bool:
        """ tc O(N) sc O(N)
        1. remove `,` and save it into arr
        2. loop through arr and check current node if is None 
        3. if yes, diff + 2 else diff -1
                 9
              -1/ \-1
               3   2
          -1  /\-1  \ -1
             4  1    6
   every indegree +1; outdegree -1   except root node,  there are two state of node: (1)empty:  1 indegree 0 out degree (2)non-empty: 1 indegree 2 outdegree
   => need to make sure each step maintain total state sum balanced   
       """
        nodes = preorder.split(',')
        diff = 1 
        for node in nodes:
            # note the order can't change otherwise we need to change the return condition since there are edge cases there are 1 node 
            diff -= 1  
            if diff < 0 :
                return False
            if node != '#':
                diff += 2

        return diff == 0

Stack Solution by building a Binary Tree

class Solution:
    def isValidSerialization(self, preorder: str) -> bool:
        """ tc O(N) sc O(N)
when you iterate through the preorder traversal string, for each char:

case 1: you see a number c, means you begin to expand a new tree rooted with c, you push it to stack

case 2.1: you see a #, while top of stack is a number, you know this # is a left null child, put it there as a mark for next coming node k to know it is being the right child.

case 2.2: you see a #, while top of stack is #, you know you meet this # as right null child, you now cancel the sub tree (rooted as t, for example) with these two-# children. But wait, after the cancellation, you continue to check top of stack is whether # or a number:

---- if a number, say u, you know you just cancelled a node t which is left child of u. You need to leave a # mark to the top of stack. So that the next node know it is a right child.

---- if a #, you know you just cancelled a tree whose root, t, is the right child of u. So you continue to cancel sub tree of u, and the process goes on and on.
       """
        nodes = preorder.split(',')
        st = [] 
        for node in nodes:
            while node =='#' and st and st[-1] == node:  #  current node is leaf node 
                st.pop()
                if not st: # stack is empty
                    return False
                st.pop() # here st pops a parent node 
            st.append(node) #if it is leaf node, push one back to compensate with cases where parent node has only one child 
        return len(st) == 1 and st[-1] == '#'