难度:简单
1. 题干
给你一个 非空 整数数组 nums ,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
示例1:
输入:nums = [2,2,1]
输出:1
示例2:
输入:nums = [4,1,2,1,2]
输出:4
示例3:
输入:nums = [1]
输出:1
提示
- 1 <= nums.length <= 3 * 104
- -3 * 104 <= nums[i] <= 3 * 104
- 除了某个元素只出现一次以外,其余每个元素均出现两次。
2. 思路
很快能想到的一个思路是:按顺序取其中的一个数,然后和其他的数字做对比,看看是否相等,如果存在相等的情况,则表示这个数不是唯一的,对比下一个数字,直到找到那个唯一数字。
伪代码如下:
// 伪代码,未测试是否能够正常编译与执行,仅做逻辑分析
int only_num(int *nums, int size)
{
int i,j;
for (i = 0; i < size; i++) { // 遍历所有的数字,取其中的第i个数字
for (j = 0; j < size; j++) { // 按顺序与其他数字比较
if (i == j) // 不和自身比较
continue;
if (nums[i] == nums[j]) // 存在与第i个数字相等的情况,说明当前数字不唯一,跳出,取第i个数字的下一个进行比较
break;
}
if (j >= size) // 当 j 达到size大小时,说明和所有数字都进行了比较,未找到相同的数字,说明此时的第i个数字是我们所需要的
break;
}
return nums[i]; // 返回第i个数字
}
上面的逻辑,空间复杂度上,只定义了i和j两个变量,所以空间复杂度是O(1)
时间复杂度
- 在最好的情况下(第一个就是唯一的数字),此时程序外层循环执行了1次,内层循环执行了n次,所以时间复杂度是O(n);
- 当考虑到最差情况(最后一个数字才是唯一数字),此时外层循环执行了n次,内层循环可能执行的次数是0~n次,所以总体来说大概是O(n2);
时间复杂度是不满足题干要求的(题干要求线性时间复杂度)。
再认真阅读题目,其中有提示说 其余每个元素均出现两次,这就想到了异或的操作:两个相等的数做异或操作后,值为0,任何数和0做异或操作后为原来的数,即:
a ^ b ^ b = b ^ b ^ a = a;
a ^ b ^ c ^ b ^ c = a;
所以最终代码可以写成:
int only_num(int *nums, int size)
{
int n = nums[0];
for (i = 1; i < size; i++) {
n ^= nums[i];
}
return n;
}