算法札记——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};
    }
};
相关推荐
ZhengEnCi1 小时前
同样的模型,为什么本地和云 API 差别那么大:量化精度、聊天模板与采样参数的三大元凶
人工智能·算法·llm
Flynn Liang1 小时前
RAG 答非所问/幻觉怎么破?Spring AI QuestionAnswerAdvisor 召回优化实战(TopK/重排/混合检索)
人工智能·算法·spring·rag·幻觉·springai
liminla!1 小时前
SQL 从入门到进阶:语法速查与实战练习(完整优化版)
数据库·sql·算法
维克兜率天3 小时前
【维克】指数调整:哪些股票会被“强制买入“?
人工智能·python·算法
罗西的思考3 小时前
【轻量化复刻大模型】线性注意力交互式演示
人工智能·算法·机器学习
神也畏惧菠萝3 小时前
Day08 Python流程控制从入门到上瘾:if/match/while/for四件套,从B站登录一路打到猜数字游戏
python·算法·数据存储·for·while·if·ifelse
揽星河..4 小时前
【C语言】分支和循环
c语言·c++·算法
Fuyo_11195 小时前
C++ AVL树保姆级详解:从平衡因子到四大旋转机制
java·c++·算法
jktantan6 小时前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript