算法札记——4.27

今天记录三道有关位运算的有趣的算法题,都是找出数组中存在的仅出现一次的数字:

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


按照题目要求是,除一个元素外,其余元素均出现了两次,然后让我们找到那个仅出现一次的元素。看到题目立即可以想到哈希表,统计所有的数据出现次数,最后将仅出现一次的数据返回即可,当然接下来的两题均可使用此方法解决。

但题目存在一个需要使用常量空间的限制条件(当然上述方法是可以过题的),若要满足该要求,显然是不能使用容器来统计数据个数的方法的。此时,便需要运用到位运算的知识:对于两个相同的数进行异或,结果为0;任何数与0异或,数值不变 。那么将数组中所有的元素进行异或到一起呢,显然最后仅出现一次的数会留下来 ,而其余两两相同的数则会因为异或而消除

以下是题解:

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (auto x : nums)
            ret ^= x;
        return ret;
    }
};

137. 只出现一次的数字 II - 力扣(LeetCode)


这题与上道的不同仅仅是其余的元素的出现次数增加到了3。但显然异或的方法是不能用的,需要另辟蹊径。此时,可想到有关数据的二进制存储:对于二进制,数位非1即0,若将n(n > 1)个相同元素,与一个不同的元素的固定比特位相加,再对和模n,不就可以得到那个不同元素在该数位上的值,因此可以:

每次,将数组中所有的数二进制的固定数位相加,最后模3,得到的结果如果为1,ret在该数位就为1;为0,ret在该数位上为0。

因为除结果元素在数组中仅出现1次,其余元素均出现了3次,所有元素的固定比特位相加模3,即可消除除结果元素的数据痕迹,只留下结果元素在该比特位的值。

以下是题解:

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (int i = 0; i < 32; ++i)
        {
            int bit = 0;
            for (auto x : nums)
                bit += ((x >> i) & 1);
            ret |= (1 << i) * (bit % 3);
        }
        return ret;
    }
};

显然,n也可以取2,此时便于前一题的条件相同,所以前一题也可以这样解决:

cpp 复制代码
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ret = 0;
        for (int i = 0; i < 32; ++i)
        {
            int bit = 0;
            for (auto x : nums)
                bit += ((x >> i) & 1);
            ret |= (1 << i) * (bit % 2);
        }
        return ret;
    }
};

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


此题与第一题的不同就是仅出现一次的元素增加到了2个。显然,前面的方法并不能直接套用解决。但运用第一题的第一个解法与二进制的存储方式(原码,反码和补码)可以提供解题思路:

(以题中例一举例)对数组所有元素进行异或得到tmp = 6,-tmp = -6,对两数的二进制表示:

tmp:> 00000000 00000000 00000000 00000110

-tmp:> 11111111 11111111 11111111 11111010

此时对两数进行&运算,可以发现除了第二数位为1,其余数位都为0。

tmp & -tmp:> 00000000 00000000 00000000 00000010

这样就可以得到原数组中结果元素相异的最小相异数位,通过该值对原数组所有元素相与来进行分组,相同的组进行异或运算,相同元素均会处在相同的组而消除,而结果两元素由于在该数数位相异而分开,最后仅有两结果元素会留下。

以下为题解:

cpp 复制代码
class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        long long tmp = 0;
        for (auto val : nums)
            tmp ^= val;
        int x = 0, y = 0;
        /*
        二进制表示(以示例一举例):
         tmp(6):  00000000 00000000 00000000 00000110
        -tmp(-6): 11111111 11111111 11111111 11111010
        想与可以得到结果的两数二进制中最小一位相异的数位
        */
        tmp &= -tmp;
        for (auto val : nums)
            (tmp & val) != 0 ? x ^= val : y ^= val;
        return {x, y};
    }
};
相关推荐
维克兜率天1 小时前
【维克】动量指标家族:RSI、ROC、CCI、Momentum全面解析
python·算法
AI情绪识别开源2 小时前
检信 AI 智能推广平台(代号:JX-Promote)
人工智能·算法·erlang
老当益壮梁奶奶3 小时前
Linux软件编程学习笔记(八):进程间通信详解(1)
linux·c语言·笔记·学习·算法
不会就选b3 小时前
算法日常・每日刷题--<BFS拓扑排序>4
算法
不正经学生3 小时前
C语言动态内存管理(上):堆上的自由与责任
c语言·开发语言·c++·算法·面试
伟大的车尔尼3 小时前
贪心的概念
数据结构·算法·贪心
方方洛3 小时前
vllm教程-00-前言与导读
人工智能·算法·vllm
IT毕设实战小研4 小时前
电影数据可视化推荐系统
大数据·后端·爬虫·python·算法·信息可视化·课程设计
且随疾风前行.4 小时前
从驱动来分析服务的添加过程
算法
CoderYanger4 小时前
A.每日一题:输入单词需要的最少按键次数 Ⅰ+Ⅱ
java·数据结构·算法·leetcode·面试