背景
上次面试,考的是LeetCode136题目,真要是实现,其实方法会比较多(遍历或记录),但明显考点不在此;自己的思路是需要把相同的数进行中和(此前做过类似的题目),不过最后还是没有搞出来,最后面试官提示了使用异或操作;虽然此前在刷leetcode时有做过类似的题目,但是印象不深,也没有太理解异或操作的特点,所以导致这次简单的题目fail了。今天单拿出来又仔细练习了一番。
异或的特点
1)0^n=n;解释:0与任何数异或的结果为该数值;
2)n^n=0;解释:两个相等的值进行异或,则结果为0。
所以说,通过异或操作,就能实现将相同的数值进行中和的效果。
题目
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
示例 1:
输入: [2,2,1]
输出: 1
示例 2:
输入: [4,1,2,1,2]
输出: 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/single-number
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
答案
public int singleNumber1(int[] nums) {
// 1 0^n=n;
// 2 n^n=0
int result = 0;
for (int i : nums) {
result ^= i;
}
return result;
}
题目进阶
如果输入参数有2个数只出现1次,而其他数值都会出现2次,找出这两个只出现1次的数值;例如,[1,2,2,1,3,4],目的找到[3,4]。
思路:
1.需要想办法将输入数组分成两组,同时确保把3和4分开到两个组中;
1.1 如何做到将3,4分开放置到2个组中?找出3,4数值的差异部分(该题目中是3,4),然后用该部分来区分3和4;
1.2 如何找到3,4的差异部分,可以对3,4进行异或操作,相同部分的二进制会为0,而不同的部分会为1;
1.3 因为除了3,4之外的其他数值都会出现两次,所以说对所有数值在进行异或之后,其结果仍旧是3,4的差异部分(异或的原因);
1.4 使用异或的结果中的任意1位非0位,对输入数组进行分组即可达到目的。
2.在将输入数值分成2个组之后,3和4会分别在2个组中,再分别对两个组进行异或操作即可把两个组中不同的数值取出来。
public int singleNumber1(int[] nums) {
// 1 0^n=n;
// 2 n^n=0
int result = 0;
for (int i : nums) {
result ^= i;
}
return result;
}
public int[] split(int[] nums) {
int[] result = new int[2];
// 计算出所有数值的异或的结果
int result1 = singleNumber1(nums);
int result2 = 0x80; // 1000 0000
// 找到左侧第一个二进制不为0的数值,用于对输入数组进行分组
while((result2 & result1) == 0) {
result2 >>= 1;
}
List<Integer> left = new ArrayList<>();
List<Integer> right = new ArrayList<>();
// 对输入数组进行分组
for (int i : nums) {
if ((i & result2) == 0) {
left.add(i);
} else {
right.add(i);
}
}
// 对两个数组分别进行异或操作,以计算出2个只出现1次的数值
result[0] = singleNumber1(left.stream().mapToInt(Integer::valueOf).toArray());
result[1] = singleNumber1(right.stream().mapToInt(Integer::valueOf).toArray());
return result;
}