剑指–数组中数字出现的次数
1,题目:
2,思路:
方法一:异或运算
由于数组中存在着两个数字不重复的情况,我们将所有的数字异或操作起来,最终得到的结果是这两个数字的异或结果:(相同的两个数字相互异或,值为0,不同的异或,值为1)) 最后结果一定不为0,因为有两个数字不重复。
(1)首先,得到异或结果,即为不相同两个数的异或结果sum(如用4 4 6 1来举例的话,这时sum为0111)
(2)再用&运算,所以我们可以根据数组元素的二进制低位第一位是否为1,将数组分为2类:
-
示例数组可以分为 低位第一位为0:[4,4,6] 低位第一位为1:[1]
-
此时再将两个数组两两异或就可以得到最终结果。
-
需要注意的是,分组的结果必然是相同的数在相同的组,且还有一个结果数
-
因此每组的数再与res=0一路异或下去,最终会得到那个结果数A或B
-
且由于异或运算具有自反性,因此只需得到其中一个数即可
方法二:双指针:
-
首先,对原数组进行排序 其次,新建结果数组res[],并用i指针指向这个结果数组
-
再次,用left,right指针指向原数组的起始和结束位置
-
再次,判断nums[left]和nums[left++],和nums[right]和nums[right–]是否相等来跳过重复数组
-
最后,如果相等则跳过,指针移动;若不相等就证明此时这个值就出现了一次,这个值就是想要的结果,把其赋予给结果数组res中即可。
3,代码:
方法一:异或运算
class Solution {
public int[] singleNumbers(int[] nums) {
int sum=0;
//将数组所有元素进行异或,最后的结果一定是那两个单一数字的异或结果。看上图示例
//用示例[4,4,6,1]最后的抑或结果就是 6和1异或的结果 7
for (int i = 0; i <nums.length ; i++) {
sum^=nums[i];//得到异或结果,即为不相同两个数的异或结果sum
//如用4 4 6 1来举例的话,这时sum为0111
}
int first = 1;
//通过与运算找到result第一个不为0的首位,7=>0111,也就是第一位
while((sum&first)==0){
first=first<<1;//这时候指针指到了0后面的1,也就是第一个不为0的首位
}
//first为1,所以我们可以根据数组元素的二进制低位第一位是否为1,将数组分为2类,
// 示例数组可以分为 低位第一位为0:[4,4,6] 低位第一位为1:[1]
//此时再将两个数组两两异或就可以得到最终结果。
//需要注意的是,分组的结果必然是相同的数在相同的组,且还有一个结果数
//因此每组的数再与res=0一路异或下去,最终会得到那个结果数A或B
//且由于异或运算具有自反性,因此只需得到其中一个数即可
int result[]=new int[2];
for(int i=0;i<nums.length;i++){
//将数组分类。
if((nums[i]&first)==0){//表示低位第一位为0的
result[0]^=nums[i];
}
else{//表示低位第一位为1的
result[1]^=nums[i];
}
}
return result;
}
}
方法二:双指针:
class Solution {
public int[] singleNumbers(int[] nums) {
Arrays.sort(nums);
int[] res = new int[2];
int i = 0;
int left = 0;
int rigth = nums.length - 1;
while(left <= rigth){
if(nums[left] != nums[left + 1] && i <2){
res[i++] = nums[left];//就表示这个值就出现了一次,所以把放入到结果数组中
left++;
}else{
left += 2;//这是从左边跳过重复元素
}
if(nums[rigth] != nums[rigth - 1] && i<2){
res[i++] = nums[rigth];//就表示这个值就出现了一次,所以把其放入到结果数组中
rigth--;
}else{
rigth -= 2;//这是从右边跳过重复元素
}
}
return res;
}
}