统计二叉树中的伪回文路径 : 用位运用来加速??

题目描述

这是 LeetCode 上的 1457. 二叉树中的伪回文路径 ,难度为 中等

Tag : 「DFS」、「位运算」

给你一棵二叉树,每个节点的值为 19

我们称二叉树中的一条路径是 「伪回文」的,当它满足:路径经过的所有节点值的排列中,存在一个回文序列。

请你返回从根到叶子节点的所有路径中伪回文路径的数目。

示例 1:

ini 复制代码
输入:root = [2,3,1,3,1,null,1]

输出:2 

解释:上图为给定的二叉树。总共有 3 条从根到叶子的路径:红色路径 [2,3,3] ,绿色路径 [2,1,1] 和路径 [2,3,1] 。
     在这些路径中,只有红色和绿色的路径是伪回文路径,因为红色路径 [2,3,3] 存在回文排列 [3,2,3] ,绿色路径 [2,1,1] 存在回文排列 [1,2,1] 。

示例 2:

ini 复制代码
输入:root = [2,1,1,1,3,null,null,null,null,null,1]

输出:1 

解释:上图为给定二叉树。总共有 3 条从根到叶子的路径:绿色路径 [2,1,1] ,路径 [2,1,3,1] 和路径 [2,1] 。
     这些路径中只有绿色路径是伪回文路径,因为 [2,1,1] 存在回文排列 [1,2,1] 。

示例 3:

ini 复制代码
输入:root = [9]

输出:1

提示:

  • 给定二叉树的节点数目在范围 <math xmlns="http://www.w3.org/1998/Math/MathML"> [ 1 , 1 0 5 ] [1, 10^5] </math>[1,105] 内
  • <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 < = N o d e . v a l < = 9 1 <= Node.val <= 9 </math>1<=Node.val<=9

DFS + 位运算

"伪回文"是指能够通过重新排列变成"真回文",真正的回文串只有两种情况:

  • 长度为偶数,即出现次数为奇数的字符个数为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 0 0 </math>0 个
  • 长度为奇数,即出现次数为奇数的字符个数为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1 个(位于中间)

因此,我们只关心路径中各个字符(数字 0-9)出现次数的奇偶性,若路径中所有字符出现次数均为偶数,或仅有一个字符出现次数为奇数,那么该路径满足要求

节点值范围为 <math xmlns="http://www.w3.org/1998/Math/MathML"> [ 1 , 9 ] [1, 9] </math>[1,9],除了使用固定大小的数组进行词频统计以外,还可以使用一个 int 类型的变量 cnt 来统计各数值的出现次数奇偶性:若 <math xmlns="http://www.w3.org/1998/Math/MathML"> c n t cnt </math>cnt 的第 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k 位为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1,说明数值 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k 的出现次数为奇数,否则说明数值 <math xmlns="http://www.w3.org/1998/Math/MathML"> k k </math>k 出现次数为偶数或没出现过,两者是等价的。

例如 <math xmlns="http://www.w3.org/1998/Math/MathML"> c n t = ( 0001010 ) 2 cnt = (0001010)_2 </math>cnt=(0001010)2 代表数值 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1 和数值 <math xmlns="http://www.w3.org/1998/Math/MathML"> 3 3 </math>3 出现次数为奇数次,其余数值没出现过或出现次数为偶数次。

翻转一个二进制数字中的某一位可使用「异或」操作,具体操作位 cnt ^= 1 << k

判断是否最多只有一个字符出现奇数次的操作,也就是判断一个二进制数字是为全为 <math xmlns="http://www.w3.org/1998/Math/MathML"> 0 0 </math>0 或仅有一位 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1,可配合 lowbit 来做,若 cntlowbit(cnt) = cnt & -cnt 相等,说明满足要求。

考虑到对 lowbit(x) = x & -x 不熟悉的同学,这里再做简单介绍:lowbit(x) 表示 x 的二进制表示中最低位的 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1 所在的位置对应的值 ,即仅保留从最低位起的第一个 <math xmlns="http://www.w3.org/1998/Math/MathML"> 1 1 </math>1,其余位均以 <math xmlns="http://www.w3.org/1998/Math/MathML"> 0 0 </math>0 填充:

  • x = 6,其二进制表示为 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 110 ) 2 (110)_2 </math>(110)2,那么 <math xmlns="http://www.w3.org/1998/Math/MathML"> l o w b i t ( 6 ) = ( 010 ) 2 = 2 lowbit(6) = (010)_2 = 2 </math>lowbit(6)=(010)2=2
  • x = 12,其二进制表示为 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( 1100 ) 2 (1100)_2 </math>(1100)2,那么 <math xmlns="http://www.w3.org/1998/Math/MathML"> l o w b i t ( 12 ) = ( 100 ) 2 = 4 lowbit(12) = (100)_2 = 4 </math>lowbit(12)=(100)2=4

Java 代码:

Java 复制代码
class Solution {
    int ans = 0;
    public int pseudoPalindromicPaths (TreeNode root) {
        dfs(root, 0);
        return ans;
    }
    void dfs(TreeNode root, int cnt) {
        if (root.left == null && root.right == null) {
            cnt ^= 1 << root.val;
            if (cnt == (cnt & -cnt)) ans++;
            return ;
        }
        if (root.left != null) dfs(root.left, cnt ^ (1 << root.val));
        if (root.right != null) dfs(root.right, cnt ^ (1 << root.val));
    }
}

C++ 代码:

C++ 复制代码
class Solution {
public:
    int ans;
    int pseudoPalindromicPaths(TreeNode* root) {
        dfs(root, 0);
        return ans;
    }
    void dfs(TreeNode* root, int cnt) {
        if (!root->left && !root->right) {
            cnt ^= 1 << root->val;
            if (cnt == (cnt & -cnt)) ans++;
            return;
        }
        if (root->left) dfs(root->left, cnt ^ (1 << root->val));
        if (root->right) dfs(root->right, cnt ^ (1 << root->val));
    }
};

Python 代码:

Python 复制代码
class Solution:
    def pseudoPalindromicPaths (self, root: Optional[TreeNode]) -> int:
        ans = 0
        def dfs(root, cnt):
            nonlocal ans
            if not root.left and not root.right:
                cnt ^= 1 << root.val
                ans += 1 if cnt == (cnt & -cnt) else 0
                return 
            if root.left:
                dfs(root.left, cnt ^ (1 << root.val))
            if root.right:
                dfs(root.right, cnt ^ (1 << root.val))
        dfs(root, 0)
        return ans

TypeScript 代码:

TypeScript 复制代码
function pseudoPalindromicPaths (root: TreeNode | null): number {
    let ans = 0;
    const dfs = function (root: TreeNode, cnt: number): void {
        if (root.left == null && root.right == null) {
            cnt ^= 1 << root.val;
            if (cnt == (cnt & -cnt)) ans++;
            return ;
        }
        if (root.left) dfs(root.left, cnt ^ (1 << root.val));
        if (root.right) dfs(root.right, cnt ^ (1 << root.val));
    }
    dfs(root, 0);
    return ans;
};
  • 时间复杂度: <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( n ) O(n) </math>O(n)
  • 空间复杂度: <math xmlns="http://www.w3.org/1998/Math/MathML"> O ( log ⁡ n ) O(\log{n}) </math>O(logn)

最后

这是我们「刷穿 LeetCode」系列文章的第 No.1457 篇,系列开始于 2021/01/01,截止于起始日 LeetCode 上共有 1916 道题目,部分是有锁题,我们将先把所有不带锁的题目刷完。

在这个系列文章里面,除了讲解解题思路以外,还会尽可能给出最为简洁的代码。如果涉及通解还会相应的代码模板。

为了方便各位同学能够电脑上进行调试和提交代码,我建立了相关的仓库:github.com/SharingSour...

在仓库地址里,你可以看到系列文章的题解链接、系列文章的相应代码、LeetCode 原题链接和其他优选题解。

更多更全更热门的「笔试/面试」相关资料可访问排版精美的 合集新基地 🎉🎉

相关推荐
Estar.Lee3 小时前
查手机号归属地免费API接口教程
android·网络·后端·网络协议·tcp/ip·oneapi
LNTON羚通3 小时前
摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现
算法·目标检测·音视频·监控·视频监控
y先森3 小时前
CSS3中的伸缩盒模型(弹性盒子、弹性布局)之伸缩容器、伸缩项目、主轴方向、主轴换行方式、复合属性flex-flow
前端·css·css3
前端Hardy3 小时前
纯HTML&CSS实现3D旋转地球
前端·javascript·css·3d·html
susu10830189113 小时前
vue3中父div设置display flex,2个子div重叠
前端·javascript·vue.js
IT女孩儿4 小时前
CSS查缺补漏(补充上一条)
前端·css
2401_857610035 小时前
SpringBoot社团管理:安全与维护
spring boot·后端·安全
哭泣的眼泪4085 小时前
解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
python·算法·django·virtualenv·pygame
凌冰_5 小时前
IDEA2023 SpringBoot整合MyBatis(三)
spring boot·后端·mybatis
码农飞飞5 小时前
深入理解Rust的模式匹配
开发语言·后端·rust·模式匹配·解构·结构体和枚举