异或的特性
- x^x=0
- 交换律 a^b=b^a
- 结合律 a^(b^c)=(a^b)^c 交换律和结合律类似加法
利用x^x=0找不同 找只出现一次的,找丢失的一个,找多出来的一个,
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
利用x^x=0 ,x^0=x,以及异或的交换律和结合律。将数组内元素逐个异或,出现两次的元素异或得0,最终结果就是出现一次的元素。
给定一个包含 [0, n]
中 n
个数的数组 nums
,找出 [0, n]
这个范围内没有出现在数组中的那个数。
进阶:
- 你能否实现线性时间复杂度、仅使用额外常数空间的算法解决此问题?
示例 1:
输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。
将0,1,2,3和0,1,3 相异或,可以看出除了丢失的数字其他数字都出现了两次,因此最终结果就是丢失的数字。怎么构造出0,1,2,3这样的序列呢?用数组下标。
public int missingNumber(int[] nums) {
int result=0;
for(int i=0;i<nums.length;i++)
{
result=result^i^nums[i];
}
return result^nums.length;
}
}
给定两个字符串 s 和 t,它们只包含小写字母。
字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。
请找出在 t 中被添加的字母。
示例 1:
输入:s = "abcd", t = "abcde"
输出:"e"
解释:'e' 是那个被添加的字母。
本题把s和t各字符相异或,最终结果就是被添加的字母,因为它只出现了一次。要注意的是字符和字符异或,字符类型会被自动转化为int类型。
public char findTheDifference(String s, String t) {
int result=0;
for(int i=0;i<s.length();i++)
{
result^=s.charAt(i);
}
for(int j=0;j<t.length();j++)
{
result^=t.charAt(j);
}
return (char)result;
}
有特殊意义的&位运算
- 找到最低有效位的1 x&(-x) 住: ~x是将x按位取反,-x=~x+1
- 将最低有效位的1清0 x&(x-1)
给定一个整数数组 nums
,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
将各元素相异或,得到的结果是3^5,我们希望把数组分成两组,3和5各在一组,相同的元素在一组,这样两组异或完的两个结果就是答案。那么关键是分组的依据怎么确定? 若3^5的某位是1,说明3和5在这一位是不同的,据此分组。
如 0011^0101=0110
找到0110最低有效位: 0110&-0110=0110&~0110+1=0110&1001+1=0110&1010=0010;
对于两元素二进制表示的第二位,3是1,5是0,据此区分。
public int[] singleNumber(int[] nums) {
int x=0;
for(int i=0;i<nums.length;i++)
{
x^=nums[i];
}
x=x&(-x); //得到两个不同元素异或结果不同的最低一位
int []result=new int[2];
for(int i=0;i<nums.length;i++)
{
if((nums[i]&x)==0) //该位是0的
{
result[0]^=nums[i];
}
else //该位是1的
{
result[1]^=nums[i];
}
}
return result;
}
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
进阶:
- 给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
- 要求算法的空间复杂度为O(n)。
算法:动态规划 dp[i]=dp[i消去最低有效位的1]+1; 通过x&x-1消去最低有效位
public int[] countBits(int num) {
int[]dp=new int[num+1];
dp[0]=0;
for(int i=1;i<=num;i++)
{
dp[i]=dp[i&(i-1)]+1;
}
return dp;
}
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。
给出两个整数 x
和 y
,计算它们之间的汉明距离。
统计x^y二进制位1的数目,除了遍历各位统计,还可以不断小区最低有效位的1,直到结果为x^y为0;
public int hammingDistance(int x, int y) {
int tmp=x^y;
int distance=0;
while(tmp!=0)
{
distance++;
tmp=tmp&(tmp-1);
}
return distance;
}
构造特殊Mask,将某位置0或1;
- 获取第n位的值 x>>>(n-1)&1
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
0或1代表每个元素不选或选,所以 111 表示123,000 表示[] 000-111就是所有子集 。 时间复杂度O(2^n*n);
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> result=new ArrayList<>();
int maxnum=(int)Math.pow(2,nums.length)-1;
for(int i=0;i<=maxnum;i++)
{
List<Integer> tmp=new ArrayList<>();
int cur=i;
for(int j=0;j<nums.length;j++)
{
if((cur&1)==1) //逐个获取每一位的值
{
tmp.add(nums[j]);
}
cur=cur>>>1;
}
result.add(tmp);
}
return result;
}