【算法专题训练】37、前缀树&二叉树

1、LCR 067. 数组中两个数的最大异或值

题目信息:

cpp 复制代码
给定一个整数数组 nums ,返回 nums[i] XOR nums[j] 的最大运算结果,其中 0 ≤ i ≤ j < n 。

示例 1:
输入:nums = [3,10,5,25,2,8]
输出:28
解释:最大运算结果是 5 XOR 25 = 28.

示例 2:
输入:nums = [0]
输出:0

示例 3:
输入:nums = [2,4]
输出:6

示例 4:
输入:nums = [8,10,2]
输出:10

示例 5:
输入:nums = [14,70,53,83,49,91,36,80,92,51,66,70]
输出:127

提示:
1 <= nums.length <= 2 * 105
0 <= nums[i] <= 231 - 1
进阶:你可以在 O(n) 的时间解决这个问题吗?

解题思路1:

  • 1、审题:输入一个整数数组,要求对数组中的两个数字进行异或操作xor,并求出最终得到的异或值最大的数值并返回,
  • 注意两个数字可以相同,所以异或值最小也比当前的遍历值要大
  • 2、解题:暴力解法,遍历数组中的元素,两两进行异或xor操作,并最出最大值

代码实现1:

cpp 复制代码
int findMaximumXOR1(vector<int> &nums)
{
  int res = 0;
  int size = nums.size();
  for (int i = 0; i < size; i++)
  { // 两层for循环
    int num1 = nums[i];
    for (int j = i + 1; j < size; j++)
    {
      int num2 = nums[j];
      int xorRes = num1 ^ num2;
      res = max(res, xorRes);
    }
  }
  return res;
}

解题思路2:

  • 解法2:前缀树解法
  • 将整数num转换成数位保存到前缀树中,数位是二进制,保存的数字不是0就是1,所以前缀树的子节点只有两个,
    • 每个整数int类型为32位,如果是正整数,则最左边数位为0。
  • 先将数组中每个int整数插入到前缀树中,根据他的数位值来创建节点,从最高位到最低位
  • 查找两个数字的最大异或值,先遍历数字,然后查找与之对应的数位是否存在,如果存在则选中这个节点并且异或值增加1
    • 如果没有说明这个数位位置存在相同的数字节点,异或值结果不能增加1,
  • 问题的关键在于,查找异或值的过程中,下一个节点为异或值的目标节点,而异或值在最高位其值越高,
    • 所以异或值的结点从最高位开始就要选择值不同的结点,然后往下一直选择不同值的结点,如果没有不同值结点则选中相同值结点,异或值结果位数值不增加只往右移动

代码实现2:

cpp 复制代码
/**
 * 位数前缀树
 * - 只有两个节点,使用长度为2的数组保存节点
 */
class TrieNode
{
public:
  TrieNode *childred[2];

  TrieNode()
  {
    for (int i = 0; i < 2; i++)
    {
      this->childred[i] = nullptr;
    }
  }

  ~TrieNode()
  {
    for (int i = 0; i < 2; i++)
    {
      this->childred[i] = nullptr;
    }
  }
};

TrieNode *buildTrie(vector<int> &nums)
{
  TrieNode *root = new TrieNode();
  // 遍历数组,取出int数字,从最高位取出数位,然后插入到前缀树中
  for (auto num : nums)
  {
    TrieNode *node = root;
    for (int i = 31; i >= 0; --i)
    {
      int bit = (num >> i) & 1; // 数位的数字
      if (node->childred[bit] == nullptr)
      {
        node->childred[bit] = new TrieNode();
      }
      node = node->childred[bit];
    }
  }
  return root;
}

int getMaxXOR(vector<int> &nums, TrieNode *root)
{
  int res = 0;
  // 遍历数组中的整数,根据该整数的数位,从高位到低位,查找与之对应数位的数字节点是否存在,存在或不存在都从该结点的对应结点位置开始往下遍历
  for (const auto num : nums)
  {
    TrieNode *node = root;
    int xorRes = 0;
    for (int i = 31; i >= 0; --i)
    {
      int bit = (num >> i) & 1;
      // 查找与之相对的结点是否存在
      if (node->childred[1 - bit] != nullptr)
      {
        xorRes = (xorRes << 1) + 1;
        node = node->childred[1 - bit];
      }
      else
      {
        xorRes = (xorRes << 1);
        node = node->childred[bit];
      }
    }
    res = std::max(res, xorRes);
  }

  return res;
}

int findMaximumXOR(vector<int> &nums)
{
  TrieNode *root = buildTrie(nums);
  return getMaxXOR(nums, root);
}

2、总结:

  • 求两个整数的异或值,可以使用xor运算符实现,通过两层遍历,让数组中的元素两两异或操作,求得最大值。
  • 采用前缀树解法,对整数的单个位数创建树结构时,创建的是一棵二叉树,这个一定要区分开,这快理解不了的,可以通过画图辅助,从根节点到叶子结点的每条路径代表一个整数
  • 那要求两个整数的异或结果值最大,一个是进位要相反其结果才为1,二是越高位其异或值的结果越大,所以构建二叉树时,是先从最高位开始构建结点。
  • 在求取异或值时,也是从高位开始,查找与之相反的进位结果,也就是当前进位为0,那就要查找对应进位结点的值为1的结点,反之为0.
  • 最后是注意位移和进位运算,要仔细。
相关推荐
倒头就睡的小比特2 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼2 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光2 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark2 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her12 天前
并查集-听课笔记
笔记·算法·并查集
码流子2 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven2 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark2 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218612 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法