📌  相关文章
📜  二叉树中从根到叶的按位与的最大值(1)

📅  最后修改于: 2023-12-03 14:49:01.134000             🧑  作者: Mango

二叉树中从根到叶的按位与的最大值

问题描述

给定一棵二叉树,每个节点包含一个整数值,从根节点到叶节点沿着二进制表示的路径形成一个二进制数。求这些二进制数的按位与的最大值。

解决方法
解法一:暴力搜索

我们可以用深度优先搜索来递归遍历整棵二叉树,并计算每个从根节点到叶节点的路径的按位与的值,并找到最大值。

代码如下:

class Solution:
    def maxAnd(self, root: TreeNode) -> int:
        def dfs(node, total):
            if not node:
                return 0
            
            total &= node.val
            
            if not node.left and not node.right:
                return total
            
            return max(dfs(node.left, total), dfs(node.right, total))
        
        return dfs(root, float('inf'))

时间复杂度为 $O(n)$,其中 $n$ 是二叉树中节点的个数。因为我们需要遍历整棵二叉树。

解法二:位运算

我们可以先不用遍历整棵二叉树,而是直接用位运算来计算出答案。

我们假设最终的答案是 $ans$,则二叉树中的每个节点都会对 $ans$ 的二进制位进行影响。如果第 $i$ 位上的所有值都为 1,才能让 $ans$ 在该位上为 1。

具体做法有两种,一种是从高位到低位,一位一位地确定答案;另一种是从低位到高位,一次性确定整个答案。

下面我们以第一种方法为例来讲解。

对于二叉树中的每个节点,我们可以计算出其左子树和右子树中的所有二进制数在第 $i$ 位上出现的次数 $c_l$ 和 $c_r$,这两个数加起来就是第 $i$ 位上所有数出现的次数 $c$。

如果 $c$ 不为 $2^n$,其中 $n$ 为整数,那么第 $i$ 位上的值一定为 0。

如果 $c$ 为 $2^n$,则第 $i$ 位上的值可能为 0 或 1,取决于左右子树中的二进制数在该位上是否相同。如果相同,就让第 $i$ 位上的值为 1;否则,第 $i$ 位上的值为 0。

代码如下:

class Solution:
    def maxAnd(self, root: TreeNode) -> int:
        ans = 0
        
        for i in range(31, -1, -1):
            cl = cr = clv = crv = 0
            
            stack = [(root, 1 << i)]
            
            while stack:
                node, mask = stack.pop()
                if not node:
                    continue
                
                if mask & node.val:
                    clv += mask
                    cl += 1
                
                if clv == crv and cl == cr:
                    ans += mask
                
                if not node.left and not node.right:
                    crv += mask
                    cr += 1
                
                stack.append((node.left, mask >> 1))
                stack.append((node.right, mask >> 1))
            
            if cl == cr == 2 ** (i + 1):
                ans += min(clv, crv)
        
        return ans

时间复杂度为 $O(n)$,其中 $n$ 是二叉树中节点的个数。因为我们需要遍历整棵二叉树。

解法三:Trie 树

我们也可以使用 Trie 树来计算答案。对于每个数,在 Trie 树上从高位到低位进行遍历,如果一个节点的两个子节点均存在,则必定会出现从根节点到该节点的路径上经过某一层次的两个路径,它们的按位与的值不同。

我们可以递归遍历 Trie 树来计算出所有按位与的值,从而得到最大值。

代码如下:

class Node:
    def __init__(self):
        self.children = {}
        self.is_terminal = False

class Trie:
    def __init__(self):
        self.root = Node()
    
    def insert(self, val):
        node = self.root
        for bit in format(val, '032b'):
            if bit not in node.children:
                node.children[bit] = Node()
            node = node.children[bit]
        node.is_terminal = True
    
    def dfs(self, node):
        if not node:
            return (0, 0)
        
        lval, lcnt = self.dfs(node.children.get('0'))
        rval, rcnt = self.dfs(node.children.get('1'))
        
        if node.is_terminal:
            if lcnt == rcnt:
                return (lval | rval, lcnt + 1)
            else:
                return (0, 0)
        else:
            cnt = lcnt if rval == 0 else rcnt if lval == 0 else 0
            return ((lval & rval) << 1 | lval | rval, cnt)
        
class Solution:
    def maxAnd(self, root: TreeNode) -> int:
        trie = Trie()
        for node in list(dfs(root)):
            trie.insert(node)
        return trie.dfs(trie.root)[0]
    
def dfs(node):
    if not node:
        return
    
    yield node.val
    
    yield from dfs(node.left)
    yield from dfs(node.right)

时间复杂度为 $O(n)$,其中 $n$ 是二叉树中节点的个数。因为我们需要遍历整棵二叉树并构建 Trie 树。