【位运算】5 道经典入门 LeetCode 位运算题整理

【位运算】5 道经典入门 LeetCode 位运算题整理

前言

最近学习位运算,一开始觉得很抽象、难理解,慢慢刷题后才发现:位运算其实就几个固定技巧,题目都是套路。这篇是我的学习整理,适合和我一样刚入门的同学。

6 个常用位运算核心技巧

这些技巧不用一下子全懂,做题时慢慢体会:

  1. 异或^:相同为0,不同为1,任何数和自己异或为0。作用:去重、查找只出现一次的数字
  2. 获取第 i 位:n & (1 << i)
  3. 消除最后一个1:n & (n - 1)。作用:统计 1 的个数、判断 2 的幂
  4. 保留最后一个1:n & -n
  5. 把第 i 位设为1:res |= (1 << i)
  6. 左移<<、右移>>:等价于乘 2 / 除 2

一、只出现一次的数字

题目

136. 只出现一次的数字 给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间

思路

利用异或抵消

  • 出现两次的数异或 → 0
  • 0 异或答案 → 答案所以把数组全部异或一遍,剩下的就是只出现一次的数字。

代码

class Solution { public: int singleNumber(vector<int>& nums) { int len = nums.size(); int res = 0; for (int i = 0; i < len; i++) { res ^= nums[i]; } return res; } };

二、颠倒二进制位

题目

190. 颠倒二进制位 颠倒给定的 32 位有符号整数的二进制位。

这道题在 LeetCode 190.颠倒二进制位 | 从暴力解法到位运算魔法-ZEEKLOG博客 中有详细讲解。

思路

一位一位取出来,再一位一位放到结果里,相当于把二进制 “倒过来”。

代码

class Solution { public: uint32_t reverseBits(uint32_t n) { uint32_t res = 0; for (uint32_t i = 0; i < 32; i++) { uint32_t bit = n >> i & 1; // 从右往左取n的每一位数字 res <<= 1; // res整体往左挪一位,空出最右边的位置 res |= bit; // 将取出的数字放在res的最右边 } return res; } };

三、位1的个数

题目

191. 位1的个数 给定一个正整数 n,编写一个函数,获取一个正整数的二进制形式并返回其二进制表达式中 设置位 的个数(也被称为汉明重量)。

思路

技巧:n & (n-1) 会消除最后一个 1

消除几次,就有几个 1。

代码

class Solution { public: int hammingWeight(int n) { int res = 0; while (n) { n &= n - 1; // 消除最后一个1 res++; // 消除次数=1的个数 } return res; } };

四、2的幂

题目

231. 2 的幂 给你一个整数 n,请你判断该整数是否是 2 的幂次方。如果是,返回 true ;否则,返回 false 。如果存在一个整数 x 使得 n == 2x ,则认为 n 是 2 的幂次方。

思路

2的幂的二进制特点:有且只有一个1

消除最后一个 1 后一定变成 0,同时要保证 n > 0

代码

class Solution { public: bool isPowerOfTwo(int n) { if (n <= 0) { // 只有正整数才能为2的幂次方 return false; } n &= (n - 1); // 消除最后一个1 return n == 0; // 2的幂次方只有一个1,消除之后n=0 } };

五、丢失的数字

题目

268. 丢失的数字 给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

思路

依旧是异或去重:可以使res(初始为n)异或0~n-1,同时异或数组的元素,最终这个数的结果就是丢失的数字。因为数组中的存在的数字被异或了两次,只有丢失的数字只被异或了一次。

代码

class Solution { public: int missingNumber(vector<int>& nums) { int n = nums.size(); int res = n; for (int i = 0; i < n; i++) { res ^= i;// 异或0~n-1 res ^= nums[i];// 异或数组中的数字 } return res; } };

学习小结

位运算刚学的时候确实不太习惯,但刷完这几道题发现:题目考来考去,都是那几个固定技巧。

  • 看到重复、消失、只出现一次 → 想异或
  • 看到统计 1、2 的幂 → 想 n & (n-1)
  • 看到二进制翻转 → 想移位 + 取位

继续多练几道,应该就能越来越熟练了。

Read more

《算法闯关指南:优选算法--滑动窗口》--15.串联所有单词的子串,16.最小覆盖子串

《算法闯关指南:优选算法--滑动窗口》--15.串联所有单词的子串,16.最小覆盖子串

🔥草莓熊Lotso:个人主页 ❄️个人专栏:《C++知识分享》《Linux 入门到实践:零基础也能懂》 ✨生活是默默的坚持,毅力是永久的享受。 🎬博主简介: 目录 前言: 15. 串联所有单词的子串 解法(滑动窗口+哈希表): 算法思路: C++算法代码: 算法总结&&笔记展示: 16. 最小覆盖子串 解法 (滑动窗口+哈希表): 算法思路: 算法流程: C++算法代码: 初版: 优化版: 算法总结&&笔记展示: 结尾: 前言: 聚焦算法题实战,系统讲解三大核心板块:优选算法:剖析动态规划、二分法等高效策略,学会寻找“最优解”。 递归与回溯:

By Ne0inhk
【优选算法】双指针算法:专题二

【优选算法】双指针算法:专题二

目录 【611.有效三角形个数】 1、题目描述 2、实现核心及思路 解题步骤: 思路可视化: 代码实现: 【179.查找总价格为目标值的两个商品】 1、题目描述: 2、实现核心及思路: 代码实现: 【15.三数之和】 1、题目描述: 2、实现核心及思路: 解题步骤: 思路可视化: 代码实现: 【18.四数之和】 1、题目描述: 编辑2、实现核心即思路: 解题步骤: 代码实现: 【611.有效三角形个数】 1、题目描述 2、实现核心及思路 构成三角形的条件:设三角形三边长分别为a(最长边),b(最短边),c。 则有 a + b >

By Ne0inhk
优选算法——双指针专题 3.快乐数 4.盛水最多的容器

优选算法——双指针专题 3.快乐数 4.盛水最多的容器

优选算法——双指针专题 3.快乐数 4.盛水最多的容器 一.快乐数 1.题目解析 [题目传送门](202. 快乐数 - 力扣(LeetCode)) 2.原理解析 第一种情况:数最后变成1 第二种情况:无限循环但不是1 但两种都可以抽象成一种,有点像之前做过的带环链表 解法:快慢双指针 1.定义快慢指针 2.慢指针每次向后移动一步,快指针每次向后移动两步 3.判断相遇时候的值 3.代码实现 classSolution{public:intBitSum(int n)//返回每一位数上的平方和{int sum=0;while(n){int m=n%10;

By Ne0inhk
Flutter for OpenHarmony:diffutil_dart 列表差异计算引擎,高性能 UI 局部刷新的秘密武器(Myers 算法) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:diffutil_dart 列表差异计算引擎,高性能 UI 局部刷新的秘密武器(Myers 算法) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在 Flutter 开发中,我们经常遇到列表更新的场景: * 用户下拉刷新,服务器返回了新的 20 条数据,其中 18 条是旧的,2 条是新的,还有 1 条被删除了。 * 我们需要更新 ListView 或 SliverList。 直接调用 setState 重新构建整个 List 确实简单,但性能有损耗,而且会导致 Scroll 位置丢失、动画生硬。我们希望能够: * 只插入那 2 条新数据。 * 只移除那 1 条旧数据。 * 并伴随优雅的插入/移除动画(使用 AnimatedList)。 diffutil_dart 就是解决这个问题的算法库。

By Ne0inhk