【LeetCode 每日一题】3314. 构造最小位运算数组 I —— (解法二)

Problem: 3314. 构造最小位运算数组 I

文章目录

  • [1. 整体思路与数学推导](#1. 整体思路与数学推导)
  • [2. 完整代码](#2. 完整代码)
  • [3. 时空复杂度](#3. 时空复杂度)
      • [时间复杂度: O ( N ) O(N) O(N)](#时间复杂度: O ( N ) O(N) O(N))
      • [空间复杂度: O ( N ) O(N) O(N)](#空间复杂度: O ( N ) O(N) O(N))

1. 整体思路与数学推导

核心观察

方程 x ∣ ( x + 1 ) = num x \mid (x + 1) = \text{num} x∣(x+1)=num 的位运算行为如下:

  • 加法进位 : x + 1 x + 1 x+1 会找到 x x x 二进制表示中从最低位开始连续的所有的 1 ,把它们变成 0,并将紧邻的高位 0 变成 1。
    • 设 x = ... 0 11 ... 1 ⏟ k 个 x = \dots 0 \underbrace{11\dots1}_{k \text{个}} x=...0k个 11...1.
    • 则 x + 1 = ... 1 00 ... 0 ⏟ k 个 x+1 = \dots 1 \underbrace{00\dots0}_{k \text{个}} x+1=...1k个 00...0.
  • 或运算 : x ∣ ( x + 1 ) x \mid (x + 1) x∣(x+1) 会合并这两个结果。
    • x ∣ ( x + 1 ) = ... 1 11 ... 1 ⏟ k 个 x \mid (x+1) = \dots 1 \underbrace{11\dots1}_{k \text{个}} x∣(x+1)=...1k个 11...1.
    • 结论:运算结果 num 的二进制形式一定是:某一位 0 被变成了 1,且其右侧(低位)原本全是 1

逆向求解

给定 num,我们要还原最小的 x

  1. 奇偶性检查

    • 如果 num 是偶数,二进制末尾是 0。由于 x ∣ ( x + 1 ) x \mid (x+1) x∣(x+1) 必然把最低位的 0 填满成 1 甚至是更高的进位,结果一定是奇数。
  2. 寻找模式

    • num 的二进制形式必然是 ...011...1
    • 我们要找的 x 就是把这串连续 1最左边(最高位)的那个 1 变回 0
    • 例如:num = 10111 (23)。
      • 末尾连续的 1111
      • 我们要找的 x10011 (19)。
      • 验证: 19 ∣ 20 = 10011 ∣ 10100 = 10111 = 23 19 | 20 = 10011 | 10100 = 10111 = 23 19∣20=10011∣10100=10111=23。对!
  3. 位运算技巧

    • 取反 ~x:把末尾的连续 1 变成连续 0,紧邻的高位 0 变成 1
      • x = ...0111, ~x = ...1000.
    • Lowbit t & -t:提取二进制中最右侧的 1
      • 对于 ~x,最右侧的 1 恰好对应原数 x第一个非 1 的位置(即连续 1 左边的那个 0)
      • 但这还不是我们想要的。我们需要定位的是连续 1 中最高位的那个 1
    • 实际逻辑调整
      • 代码中的 lowbit 实际上找的是 x最低位的 0 所对应的权值。
      • 例如 x = 10111 (23)。~x 末尾是 ...01000lowbit8 (1000)。
      • 这个 8 对应的是 x 中的那个 0
      • 我们要消除的 1 是这个 0 右边一位1
      • 所以:lowbit >> 1 就是我们要消除的那个 1 的掩码。
      • x ^ (lowbit >> 1):通过异或操作,将该位翻转(从 1 变 0)。

2. 完整代码

java 复制代码
import java.util.List;

class Solution {
    public int[] minBitwiseArray(List<Integer> nums) {
        int n = nums.size();
        int[] ans = new int[n];
        
        for (int i = 0; i < n; i++) {
            int x = nums.get(i);
            
            // 特判:如果 x 是 2 (二进制 10),无法由 x|(x+1) 生成
            // 因为偶数(除了可能的位溢出情况)通常无法作为结果
            // 题目可能设定 x=2 时无解
            if (x == 2) {
                ans[i] = -1;
            } else {
                // 1. 取反:~x
                // 假设 x = ...0111 (二进制)
                // 则 ~x = ...1000
                int t = ~x;
                
                // 2. 求 lowbit:t & -t
                // 提取出 ~x 中最低位的 1。
                // 这个 1 对应的是原数 x 中从低位向高位看的"第一个 0"的位置。
                // 比如 x=...0111,这个 0 就是 111 左边的那个位。
                int lowbit = t & -t;
                
                // 3. 计算结果:x ^ (lowbit >> 1)
                // lowbit >> 1 得到的是 x 中"连续 1 序列"的最高位。
                // 用异或 (^) 将该位从 1 变成 0,即得到了满足条件的最小 x。
                // 例如 x = 10111 (23), 0 在第 4 位(值8), lowbit=8。
                // lowbit >> 1 = 4 (100)。
                // 23 ^ 4 = 10111 ^ 00100 = 10011 (19)。
                ans[i] = x ^ (lowbit >> 1);
            }
        }
        return ans;
    }
}

3. 时空复杂度

假设 nums 的长度为 N N N。

时间复杂度: O ( N ) O(N) O(N)

  • 计算依据
    • 代码只遍历了一次输入数组。
    • 循环内部全是位运算(取反、与、移位、异或),这些都是 CPU 的基本指令,耗时 O ( 1 ) O(1) O(1)。
    • 没有嵌套循环,没有递归。
  • 结论 : O ( N ) O(N) O(N)。这是处理此类问题的理论最优复杂度。

空间复杂度: O ( N ) O(N) O(N)

  • 计算依据
    • 使用了一个长度为 N N N 的数组 ans 来存储结果。
  • 结论 : O ( N ) O(N) O(N)。
相关推荐
leiming61 小时前
C语言联合体union的用法(非常详细,附带示例)
java·python·算法
薛定e的猫咪1 小时前
【NeurIPS 2023】多目标强化学习算法工具库-MORL-Baselines
人工智能·算法·机器学习
Sarvartha2 小时前
单链表的插入和合并以及双链表的删除
算法
Tisfy2 小时前
LeetCode 3507.移除最小数对使数组有序 I:纯模拟
算法·leetcode·题解·模拟·数组
努力学算法的蒟蒻2 小时前
day63(1.22)——leetcode面试经典150
算法·leetcode·面试
永远都不秃头的程序员(互关)2 小时前
【决策树深度探索(一)】从零搭建:机器学习的“智慧之树”——决策树分类算法!
算法·决策树·机器学习
程序员-King.2 小时前
day161—动态规划—最长递增子序列(LeetCode-300)
算法·leetcode·深度优先·动态规划·递归
西柚小萌新2 小时前
【计算机视觉CV:目标检测】--3.算法原理(SPPNet、Fast R-CNN、Faster R-CNN)
算法·目标检测·计算机视觉
高频交易dragon2 小时前
Hawkes LOB Market从论文到生产
人工智能·算法·金融