20240123-二叉树中的伪平行路径

题目要求

给定一棵二叉树,树中的节点值是 1 到 9 之间的数字。如果二叉树中的节点值至少有一个排列组合是回文,则称该路径为伪回文路径。

返回从根节点到叶节点的伪 Palindromic 路径的数目。

Example 1:

Input: root = [2,3,1,3,1,null,1]
Output: 2 
Explanation: The figure above represents the given binary tree. There are three paths going from the root node to leaf nodes: the red path [2,3,3], the green path [2,1,1], and the path [2,3,1]. Among these paths only red path and green path are pseudo-palindromic paths since the red path [2,3,3] can be rearranged in [3,2,3] (palindrome) and the green path [2,1,1] can be rearranged in [1,2,1] (palindrome).

Example 2:

Input: root = [2,1,1,1,3,null,null,null,null,null,1]
Output: 1 
Explanation: The figure above represents the given binary tree. There are three paths going from the root node to leaf nodes: the green path [2,1,1], the path [2,1,3,1], and the path [2,1]. Among these paths only the green path is pseudo-palindromic since [2,1,1] can be rearranged in [1,2,1] (palindrome).

Example 3:

Input: root = [9]
Output: 1

思路

上手一看这个题目应该是一个树上的回溯搜索,然后需要一个结构对路径上的值进行存储。同时还需要一个函数能够判断这个结构是否能够重组成为回文。

判断回文我们只需要统计一个路径上字符出现的次数,要么全部为偶数,要么只有一个数字为奇数则是回文。

然后又考虑到路径只考虑从根节点到叶子节点,所以用回溯算法也不完全正确,应该采用深度优先搜索DFS,直到搜索到根节点才结束。

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int pseudoPalindromicPaths (TreeNode* root) {
        return dfs(root, 0);
    }

    int dfs(TreeNode* node, int path) {
        if (!node) return 0;
        path ^= 1 << node->val;
        // cout << path << endl;
        if (!node->left && !node->right) {
            cout << (path & (path - 1)) << endl;
            return (path & (path - 1)) == 0;
        }
        return dfs(node->left, path) + dfs(node->right, path);
    }
};

这是我在gpt商店的Code Tutor中学习的方法,本来认为判断回文需要用一个哈希表来存储和遍历。但是这种方法实在是太出乎我预料了。

判断回文序列

在这个上下文中,"当前节点值的位"指的是一个位掩码中的一个特定位,该位代表了一个从1到9的数字。位掩码是一个整数值,其每一位(binary digit)都可以独立表示一些信息。在我们的场景中,我们使用位掩码的每一位来表示数字1到9是否在当前路径上出现了奇数次。

如何切换当前节点值的位?

当我们在路径中遇到一个数字时,我们通过执行异或操作^=和位移操作<<来切换相应的位。比如,如果节点的值是3,那么1 << node->val会得到一个只在第三位上是1的数(假设最低位是第一位),其他位都是0。执行异或操作后,这个位掩码中对应于数字3的位会切换:如果它之前是0(表示到目前为止,3出现了偶数次),它会变成1(现在出现了奇数次),反之亦然。

如何判断路径是否可以形成回文?

回文的一个关键属性是,除了最多一个字符外,其他所有字符都应该出现偶数次。在位掩码中,一个1表示对应的数字出现了奇数次,而0表示出现了偶数次。因此,如果路径可以形成回文,那么在位掩码中,至多只能有一位是1

我们通过path & (path - 1)操作来检查这一条件。这个操作的效果是将位掩码中的最低位的1变成0。如果path中只有一位是1或者没有1,那么path & (path - 1)的结果会是0

  • 如果path中没有1,那么所有数字都出现了偶数次,路径是回文。
  • 如果path中只有一位是1,那么只有一个数字出现了奇数次,其他都是偶数次,路径也是回文。
  • 如果path中有多于一位是1,那么path & (path - 1)不会是0,表示有多于一个数字出现了奇数次,路径不能形成回文。

这种方法允许我们以非常高效的方式检查一个路径是否满足回文的条件,仅通过一次位操作和比较操作就可以完成。

位运算符

  1. 按位与(&:对于每一位,只有两个操作数的相应位都是1时,结果才为1。

    • 示例:5 & 3(二进制101 & 011)等于1(二进制001)。
  2. 按位或(|:对于每一位,如果两个操作数的相应位中至少有一个为1,则结果为1。

    • 示例:5 | 3(二进制101 | 011)等于7(二进制111)。
  3. 按位异或(^:对于每一位,如果两个操作数的相应位一个是1另一个是0,则结果为1。

    • 示例:5 ^ 3(二进制101 ^ 011)等于6(二进制110)。
    • 特别地,任何数与自己异或的结果为0,与0异或的结果为自己。
  4. 按位非(~:对操作数的每一位取反。

    • 示例:~5(二进制~101)的结果为...1111111111111010(假设是32位整数,则前面的位都被置为1)。
  5. 左移(<<:将操作数的二进制表示向左移动指定的位数,右边空出的位用0填充。

    • 示例:5 << 1(二进制101左移1位)等于10(二进制1010)。
  6. 右移(>>:将操作数的二进制表示向右移动指定的位数,对于无符号类型,左边空出的位用0填充;对于有符号类型,行为依赖于实现(通常左边空出的位用符号位填充,即算术右移)。

    • 示例:5 >> 1(二进制101右移1位)等于2(二进制10)。
  • 切换位path ^= 1 << node->val;使用按位异或和左移来切换path中对应node->val的位。如果该位原来是0(即数字出现次数为偶数),它会变为1(出现次数变为奇数),反之亦然。

  • 检查是否只有一个位为1(path & (path - 1)) == 0利用按位与操作来检查path中是否至多只有一位是1。这是判断一个数是否是2的幂或者是0的常用技巧,也被用于检查路径是否可以形成伪回文。

所以回顾一下这个题目我们就能明白为什么强调,节点的值是1-9。

  • 20
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值