当前位置: 首页 > article >正文

leetCode 260.只出现一次的数字 ||| + 位运算

260. 只出现一次的数字 III - 力扣(LeetCode)


给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。


【问题思考】待解决问题(O_O)?举个栗子:xorSum -> 110,需要找到异或和中的某个值为 1 的比特位,如何解决?

  • 方式1:计算 lowbit ,只保留二进制最低位的1举个栗子
 s = 101100
~s = 010011 
(~s)+1 = 010100 // 根据补码的定义,这就是-s 最低 1 左侧取反,右侧不变
s & -s = 000100 // lowbit

  • s 和 ~s 若相与,为0

【求补码】:(~s)+ 1,可以简易操作成,其实就是从~s(取反)的结果上,从左到右,直到找到一个0后面都是连续1的子序列的位置,将其设置为1,而后面连续的1的子序列全置为0。这是求补码这个过程。然后再让 s & (~s+1) 就可以获得lowbit。

【问题思考(O_O)?】为什么s & (~s+1) 就可以获得 lowbit?原理是什么?

结合上图来看,在取反的结果(~s)上找从左到右出现 0 的最低位位置,而这个位置的左边仍然是保持 s 取反后的结果,也就是和 s 相与还是为0。那我们将 ~s这个位置设置为 1其后的设置为 0,那么 s & (~s+1) 自然就可以找到 s 出现 1 的最低位置,也就可以获得lowbit了。(因为你找到取反后的结果的 0 出现的最低位置,那么就可以通过运算变换求出 s 出现 1 的最低位置,而这恰好有求补码的过程,那么我们也可以进一步将求补码的过程简洁写成 -s )。具体操作如下:

  • ① 求补码
  • s & s的补码 => lowbit,即 s & (~s + 1)

【一个有意思的点】根据补码的定义,就是 -s,那么

  • s & (-s) => lowbit

  • 方式2:计算 s尾零的个数,直接取 nums[i] 在该比特位上的值,从而避免逻辑判断

 (1)根据 方式1:计算 lowbit ,只保留二进制最低位的1

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        vector<int>res;
        unsigned int xorSum = 0; //C++ 不需要防止溢出,用 unsigned int
        int type1=0,type2=0;
        for (const int &num: nums) {
            xorSum ^= num;
        }
        // -xorSum 等价于 对xorSum求补码,操作就是取反后加一
        // int lowbit = xorSum & (~xorSum+1);
        int lowbit = xorSum & -xorSum; 
        for (const int &num: nums) {
            //进行分组
            //把值为1的数分为一组
            if(num & lowbit) type1^=num;
            //把值为0的数分为一组
            else type2^=num;

        }
        return {type1, type2};
    }
};

 用一个vector容器来存放 type1 和 type2,也可以省去逻辑判断(if...else...)

        ...
        ...
        int type1=0,type2=0;

        int lowbit = xorSum & -xorSum; 
       
        for (const int &num: nums) {
            if(num & lowbit) type1^=num; //把值为1的数分为一组
            else type2^=num; //把值为0的数分为一组
        }
        return {type1, type2};
    }
};
-------------------------------------------------------------------------------------
        ...
        ...
        vector<int> arr(2);
        for (const int &num: nums) {
            arr[(num & lowbit)!=0] ^=num; // (num & lowbit)!=0 => true(1) or false(0)
        }
        return arr;
    }
};

于是就有如下代码: 

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        unsigned int xorSum = 0; //C++ 不需要防止溢出,用 unsigned int
        
        for (const int &num: nums) {
            xorSum ^= num;
        }

        // -xorSum 等价于 对xorSum求补码,操作就是取反后加一
        // int lowbit = xorSum & (~xorSum+1);
        int lowbit = xorSum & -xorSum; 
       
        vector<int> arr(2);
        for (const int &num: nums) {
            arr[(num & lowbit)!=0] ^=num; // (num & lowbit)!=0 => true(1) or false(0)
        }
        return arr;
    }
};

(2)根据方式2:计算 s尾零的个数,直接取 nums[i] 在该比特位上的值,从而避免逻辑判断

C++ __builtin_系列函数___builtin_ctz-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/yandaoqiusheng/article/details/102920785__builtin_ctz(x) : 返回 x 的二进制下前导的 0 的个数

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        unsigned int xorSum = 0; //C++ 不需要防止溢出,用 unsigned int
        for (const int &num: nums) {
            xorSum ^= num;
        }
        int ctz = __builtin_ctz(xorSum);
        vector<int> arr(2);
        for (const int &num: nums) {
            arr[(num >> ctz) & 1] ^=num; 
        }
        return arr;
    }
};
  • 时间复杂度:O(n),其中 n 为 nums 的长度
  • 空间复杂度:O(1),仅用到若干额外变量

参考和推荐文章:

260. 只出现一次的数字 III - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/single-number-iii/solutions/2484352/tu-jie-yi-zhang-tu-miao-dong-zhuan-huan-np9d2/分享|从集合论到位运算,常见位运算技巧分类总结! - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/circle/discuss/CaOJ45/


http://www.kler.cn/news/108771.html

相关文章:

  • 这个故事有点长 - 舟山
  • 洛谷 B2135:单词替换
  • 2022年上半年上午易错题(软件设计师考试)
  • 信息检索与数据挖掘 | 【实验】排名检索模型
  • Cesium弹窗可随地图移动
  • 神经网络与深度学习第四章前馈神经网络习题解答
  • 搞定蓝牙-第六篇(HID
  • FL Studio21.2演示版下载
  • [Leetcode] 0108. 将有序数组转换为二叉搜索树
  • unity3d场景加载
  • Sprint Cloud Stream整合RocketMq和websocket实现消息发布订阅
  • 设置Ubuntu 20.04的静态IP地址(wifi模式下)
  • 全能数字音乐工作站(DAW)编曲FL Studio21.2.0官方中文版
  • python之拟合圆心及半径
  • Opencv-图像插值与LUT查找表
  • IntelliJ IDEA 把package包展开和压缩
  • Ubuntu 22.04自动登录进入桌面
  • Error: error:0308010C:digital envelope routines::unsupported
  • YOLOv7-QAT量化部署
  • 软考系统架构师知识点集锦二:软件工程
  • 51单片机实验:数码管动态显示00-99
  • Vert.x学习笔记-异步编程和响应式系统
  • 云安全-云原生技术架构(Docker逃逸技术-特权与危险挂载)
  • 外部中断0边沿触发
  • Docker中Failed to initialize NVML: Unknown Error
  • 3DMAX快速瓦片屋顶铺设插件使用方法详解
  • JavaScript进阶知识汇总~
  • Linux--进程替换
  • C++中的成员变量内存布局
  • MinIO安装