一、题目描述
力扣链接:力扣136.只出现一次的数字
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例 1 :
输入:nums = [2,2,1]
输出:1
二、C++题解
2.1 哈希表方法
获取所有元素的哈希表,然后判断出现次数为1的那个即可。
class Solution {
public:int singleNumber(vector<int>& nums) {unordered_map<int, int> countMap;for (auto num : nums) {++countMap[num];}int target = 0;for(auto& entry : countMap) {if (entry.second == 1) {target = entry.first;break;}}return target;}
};
2.2 位运算(异或)
异或运算的两条性质:
- 一个数和它本身做异或运算结果为 0,即
a ^ a = 0
; - 一个数和 0 做异或运算的结果为它本身,即
a ^ 0 = a
。
因此,将0与nums中所有元素进行异或运算,最后得到的结果便是只出现一次的数字。
class Solution {
public:int singleNumber(vector<int>& nums) {int result = 0;for (auto num : nums) result ^= num;return result;}
};
三、扩展阅读——位运算
- 与运算(AND):对两个对应位进行比较,当两个对应位都为1时,结果为1;否则,结果为0。符号为
&
。
任何数和0做与运算,结果都是0,即 x & 0 = 0。例如,5(101) & 0 = 0。
任何数和其自身做与运算,结果是自身,即 x & x = x。例如,5(101) & 5(101) = 5(101)。
- 或运算(OR):对两个对应位进行比较,当两个对应位中至少有一个是1时,结果为1;否则,结果为0。符号为
|
。
任何数和0做或运算,结果是自身,即 x | 0 = x。例如,5(101) | 0 = 5(101)。
任何数和其自身做或运算,结果是自身,即 x | x = x。例如,5(101) | 5(101) = 5(101)。
- 异或运算(XOR):当两个对应位的值不同时,异或运算的结果为1;当两个对应位的值相同时,异或运算的结果为0。符号为
^
。
任何数和0做异或运算,结果是自身,即 x ^ 0 = x。例如,5(101) ^ 0 = 5(101)。
任何数和其自身做异或运算,结果是0,即 x ^ x = 0。例如,5(101) ^ 5(101) = 0。
异或运算满足交换律和结合律,即 a ^ b ^ c = a ^ (b ^ c) = (a ^ b) ^ c。例如,5(101) ^ 3(011) ^ 4(100) = 5 ^ (3 ^ 4) = (5 ^ 3) ^ 4。
- 非运算(NOT):对一个二进制数的每一位进行取反,将0变为1,将1变为0。符号为
~
。
非运算会反转操作数的所有位。例如,~5(101) = 2(010)。
- 左移运算(SHL):将一个二进制数的所有位向左移动指定的位数。左移操作会在二进制数的右侧补充相应数量的零。符号为
<<
。
左移n位等于乘以2的n次方,即 x << n = x * 2^n。例如,5(101) << 2 = 20(10100)。
左移运算不改变操作数的符号位。
- 逻辑右移运算(SHR):将一个二进制数的所有位向右移动指定的位数。逻辑右移操作会在二进制数的左侧补充相应数量的零。符号为
>>
。
右移n位等于除以2的n次方,即 x >> n = x / 2^n。例如,20(10100) >> 2 = 5(101)。
逻辑右移运算会用0填充移位后产生的空位。
- 算术右移运算(SAR):将一个二进制数的所有位向右移动指定的位数,但保持最高位(符号位)不变。算数右移操作会在二进制数的左侧补充相应数量的最高位(符号位)的值。符号为
>>>
。
算术右移运算会用符号位填充移位后产生的空位,因此它可以保持负数的符号。例如,对于负数-5(1011) >>> 2 = -2(1110)。