剑指 Offer 56 - I. 数组中数字出现的次数
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:
输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]
来源:力扣(LeetCode)
思路:如果把题目改成“一个数组中除了一个数字之外,其他数字都出现了两次”,那么直接遍历并异或就得到了那个数字。
两个数字的话,首先遍历并异或,得到的结果X是这两个数字num1和num2做异或运算的结果。
找到数字X中值为1的最低位,即num1和num2肯定在这个位置上不同。然后根据这个位置上的数字是0还是1,把所有的数字分成两部分。num1和num2肯定分别在其中的一个部分。分别对这两个部分做异或运算就可以得到这两个数字。
class Solution {
public int[] singleNumbers(int[] nums) {
int sum=0;
for(int i=0;i<nums.length;i++){
sum ^= nums[i];
}
int index=FindFirstBit1(sum);
int num1=0,num2=0;
for(int i=0;i<nums.length;i++){
if(Is1(nums[i],index)==1){
num1 ^=nums[i];
}else{
num2 ^=nums[i];
}
}
return new int[]{num1,num2};
}
//找到第一个是1的位置
int FindFirstBit1(int num){
int index=0;
while ((num&1)==0){
num=num>>1;
index++;
}
return index;
}
//判断某个位置是不是1
int Is1(int num, int index){
num=num>>index;
return (num&1);
}
}
一个更简洁的写法,基本思路是一致的:
class Solution {
public int[] singleNumbers(int[] nums) {
int sum=0;
for(int num:nums){
sum ^= num;
}
//通过mask把数字分为两组,首先找到mask
int mask=1;
while((sum & mask)==0){
mask<<=1;
}
int num1=0,num2=0;
for(int num:nums){
//通过和mask的且运算判断属于哪一组
if((num & mask)==0)
num1^=num;
else
num2^=num;
}
return new int[]{num1,num2};
}
}
剑指 Offer 56 - II. 数组中数字出现的次数 II
在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。
示例 1:
输入:nums = [3,4,3,3]
输出:4
示例 2:
输入:nums = [9,1,7,9,7,9,7]
输出:1
来源:力扣(LeetCode)
如果一个数字出现3次,那么它的二进制表示的每一位也出现3次,如果把所有出现三次的数字的二进制表示的每一位都分别加起来,那么每一位的和都能被3整除。
我们把数组中所有数字的二进制表示的每一位都加起来。如果某一位的和能被3整除,那么那个只出现一次的数字二进制表示中对应的那一位是0,否则是1。
class Solution {
public int singleNumber(int[] nums) {
int[] counts=new int[32];
for(int num:nums){
for(int j=0;j<32;j++){
counts[j]+=num&1;
num>>=1;
}
}
int res=0;
for(int i=0;i<32;i++){
res<<=1;
res |= counts[31-i]%3;
}
return res;
}
}
本文介绍如何在数组中找出出现一次的数字。对于两个不同数字各出现一次的情况,利用异或操作和位运算技巧解决;对于一个数字出现一次而其余数字均出现三次的情况,则通过对所有数字的每位二进制数求和后取模来确定该数字。
209

被折叠的 条评论
为什么被折叠?



