1,有一个 n 个元素的数组,除了两个数只出现一次外,其余元素都出现两次,让你找出这两个只出现一次的数分别是几,要求时间复杂度为 O(n) 且再开辟的内存空间固定(与 n 无关)。
示例 :
输入: [1,2,2,1,3,4]
输出: [3,4]
解题:(异或运算,相同位为0,不同位为1)
思路:这两个只出现一次的数字,其二进制位,一定至少有一位是不同的。
首先找到一位不同的,再将数组分为两部分,这位为1的做一次异或运算,结果是一个唯一出现一次的数字,同理,位0的也得到一个数字。
步骤1:首先将所有元素做一遍异或运算,得到一个结果,这个数字为1的就是出现一次的两个数字的不同位。
步骤2:将数组分成两组。·
步骤3:分别做异或运算。
步骤4:输出结果。
代码:
#片面解法:当两个唯一出现一次的数字的二进制相差的是最低位的一时会出现错误。而且数字大小相差较大时也报错
numbers = [3,4,1,2,1,2,3,6];
a = 0;
i = 0;
x1 = 0;
x2 = 0;
xflag = 0;
for number in numbers: #首先对数组所有元素异或处理
a ^= number; #a即是步骤1的结果
print("numbers异或所有元素结果", bin(a));
bin_str = bin(a).replace('ob','');
print(bin_str);
print(len(bin_str));
for i in range(len(bin_str)): #这是判断a的二进制中从低位像高位第一次出现1的数组下标
if bin_str[i] == "1" :
xflag = i; #数组下标
break;
for number in numbers:
bin_str = bin(number).replace('ob', '');
if bin_str[len(bin_str)-flag] == "1" : #因为每个数字的二进制位数不一样,但是从最低位到第一位为1的,位数是一样的,
x1 ^= number;
else:
x2 ^= number;
print("numbers异或所有元素结果x1=", x1);
print("numbers异或所有元素结果x2=", x2);
#修改后版本:正确
numbers = [5,6,1,2,1,2];
a = 0;
for number in numbers: #首先对数组所有元素异或处理
a ^= number; #a即是步骤1的结果
print("numbers异或所有元素结果", bin(a));
# int_num = 7;
x1 = 0;
x2 = 0;
flag = 0;
xflag = 0;
bin_str = bin(a).replace('ob','');
print(bin_str);
print(len(bin_str));
i = 0;
for i in range(len(bin_str)-1,0,-1): #这是判断a的二进制中从低位像高位第一次出现1的数组下标,range(a,b,c),a表示从什么开始,b什么时候结束,c正数时表示加c,负数时表示减
print("i = ",i);
print("bin_str[i] = ",bin_str[i]);
if bin_str[i] == "1" :
flag = len(bin_str) - i;
xflag = i;
break;
print("xflag = ",xflag);
for number in numbers:
bin_str = bin(number).replace('ob', '');
if bin_str[len(bin_str)-flag] == "1" : #因为每个数字的二进制位数不一样,但是从最低位到第一位为1的,位数是一样的,
x1 ^= number;
else:
x2 ^= number;
print("numbers异或所有元素结果x1=", x1);
print("numbers异或所有元素结果x2=", x2);
不同:步骤1的查找方式的不同。
步骤2(没用这个方法,用数组判断的): 利用按位求与运算
比如说
16位二进制数A:
1000 1000 1000 1000,
如果你想获A的哪一位
就把
数字B:
0000 0000 0000 0000的那一为设置为1.
比如说
我想获得A的第三位
就把B的第三位数字设置为1,则B为
0000 0000 0000 0100,之后A、B求与,
结果若为0,说明A的第三位为0,结果为1,说明A的第三位为1.
同理:若要获得A的第五位,
就把B设置为
0000 0000 0001 0000.
之后再求与。
通常在程序中
数字B被称为掩码,就是专门用来测试某一位是否为0的数值。
2,
题目:
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,3,2]
输出: 3
示例 2:
输入: [0,1,0,1,0,1,99]
输出: 99
思路:
关键是使数字通过某个操作,操作三次之后,能使数字回到0。就是三次一个循环。运算,操作需要你自己找规律了。
参考笔记1:python:【笔记】网上大佬曾经说,如果能设计一个状态转换电路,使得一个数出现3次时能自动抵消为0,最后剩下的就是只出现1次的数。
开始设计:一个二进制位只能表示0或者1。也就是天生可以记录一个数出现了一次还是两次。
-
x ^ 0 = x;
-
x ^ x = 0;
要记录出现3次,需要两个二进制位。那么上面单独的x就不行了。我们需要两个变量,每个变量取一位:
-
ab ^ 00 = ab;
-
ab ^ ab = 00;
这里,a、b都是32位的变量。我们使用a的第k位与b的第k位组合起来的两位二进制,表示当前位出现了几次。也就是,一个8位的二进制x就变成了16位来表示。
-
x = x[7] x[6] x[5] x[4] x[3] x[2] x[1] x[0]
-
x = (a[7]b[7]) (a[6]b[6]) ... (a[1]b[1]) (a[0]b[0])
于是,就有了这一幕....
它是一个逻辑电路,a、b变量中,相同位置上,分别取出一位,负责完成00->01->10->00,也就是开头的那句话,当数字出现3次时置零。
int singleNumber(vector<int>& nums) {
int a = 0, b = 0;
for (auto x : nums) {
a = (a ^ x) & ~b;
b = (b ^ x) & ~a;
}
return a;
}
参考笔记2:
如果能设计一个状态转换电路,使得一个数出现3次时能自动抵消为0,最后剩下的就是只出现1次的数。使用两个bit a和b分别来记录该位上实际的值~
变成2进制,1个1个bit地处理~
数字电路(状态机) 翻转逻辑:
b flip: !a
a flip: !b calculated
return b
已AC(java)代码:
public class Solution
{
public int SingleNumber(int[] nums)
{
int a = 0, b = 0;
for(int i = 0;i < nums.Length;i++)
{
b = (b ^ nums[i]) & ~a;
a = (a ^ nums[i]) & ~b;
}
return b;
}
}
代码3:
1.java解法:(一位一位的验证)
public int singleNumber(int[] nums) {
int ret = 0;
for(int i = 0; i < 32; ++i) { //三十二位二进制数字得循环32次才算结束
int bitnums = 0;
int bit = 1 << i; //1<<1=2,1<<2=4,右移相当于*2,和数字的每一位做&操作,判断这位1出现的次数。
for(int num : nums) {
if((num&bit) != 0) //&操作
bitnums++; //bitnums%3!=0时说明唯一出现的数字,在第i位,是1.
}
if(bitnums % 3 != 0) //三十二次循环中,有一个符合条件(出现1不是3的倍数时)的1将做一次或操作,32次循环后才是我们要的结果
ret |= bit;
}
return ret;
}
// 对每一位单独统计出现1的次数, 如果出现的次数不能整除3说明唯一存在的数在这一位上为1, 时间复杂度O(32N)
代码4:
2.(python解法)
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
a, b = 0, 0
for num in nums:
b = ~a & (b ^ num)
a = ~b & (a ^ num) #这两句是公式,以后可以套用,三次一循环
return b
循环详细运算步骤:(~是按位取反的符号,对数据的每个二进制位取反,即把1变为0,把0变为1)
【2,2,2,3】
1,a=00 b=00
list = 2 = 10
b = ~a & (b^list) = 11 & (00 ^ 10) = 11 & 10 =10 = 2 a=0=00取反之后~a=11
a = ~b & (a^list) = 01 & (00 ^ 10) = 01 & 10 =00 = 0 b=2=10取反之后~b=01
2,list = 2 = 10
b =~a & (b^list) = 11 &(10 ^ 10) = 11 & 00=00 a=0=00取反之后~a=11
a =~b & (a^list) = 11 &(00 ^ 10) = 11 & 10 =10 b=0=00取反之后~b=11
3,list = 2 =10
b =~a & (b^list) = 01 &(00 ^10) = 01 & 10 = 00 =0
a = ~b & (a^list) = 11 &(10 ^10) = 11 & 00 = 00 =0
循环三次,a,b=0,恢复成原值。