题目描述
一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。
示例 1:
输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]
示例 2:输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]限制:
2 <= nums.length <= 10000
方法一(计数)
1.解题思路
直接统计数组中每个数出现的次数,将出现一次的加入结果数组。思路比较简单,但是空间复杂度高。
2.代码实现
class Solution {
public int[] singleNumbers(int[] nums) {
int[] cnt=new int[10001];
int[] res=new int[2];
for(int num:nums){
cnt[num]++;
}
int id=0;
for(int num:nums){
if(cnt[num]==1){
res[id++]=num;
}
}
return res;
}
}
3.复杂度分析
- 时间复杂度:需要遍历两次数组,所以时间复杂度为O(n)。
- 空间复杂度:需要额外的计数数组,所以空间复杂度为O(n)。
方法二(异或分组)
1.解题思路
- 先将数组中所有数异或,根据异或的性质,最后的结果是两个只出现一次的数的异或
- 根据异或规则,相同为0,不同为1,找到二进制中不同的那一位是第几位
- 根据不同的这一位将原数组分为两组,分别异或得到只出现一次的两个数
2.代码实现
class Solution {
public int[] singleNumbers(int[] nums) {
int[] res=new int[2];
int m=0;
//求异或和
for(int num:nums){
m^=num;
}
int index=0;
//找不同位
for(int i=0;i<32;i++){
if(((m>>i)&1)==1){
index=i;
}
}
//根据异或分组
for(int num:nums){
if(((num>>index)&1)==1){
res[0]^=num;
}
}
res[1]=res[0]^m;
return res;
}
}
3.复杂度分析
- 时间复杂度:需要遍历两次数组,所以时间复杂度为O(n)。
- 空间复杂度:需要额外常数级别的内存空间,所以空间复杂度为O(1)。
方法三(二分法)
1.解题思路
先找到数组的最小值,最大值。然后根据这两个数进行二分,如果左边部分为0,说明是若个对相同数的异或,排除左边部分,继续往后找;如果左边不为0,右边也不为0,说明所求的两个数正好分在不同的组,直接返回;如果左边不为0,右边为0,可以排除右边部分,继续往前找。
特殊情况:当0只出现一次时,一个是0,一个是所有数相异或。
2.代码实现
class Solution {
public int[] singleNumbers(int[] nums) {
int min=Integer.MAX_VALUE,max=Integer.MIN_VALUE,sum=0,countOfZero=0;
for(int num:nums){
if(num==0){
countOfZero+=1;
}
if(min>num){
min=num;
}
if(max<num){
max=num;
}
sum^=num;
}
if(countOfZero==1){
return new int[]{sum,0};
}
int lo=min,hi=max;
while(lo<=hi){
//防止越界
int mid=lo<0&&hi>0?(lo+hi)/2:lo+(hi-lo)/2;
int l=0,r=0;
for(int num:nums){
if(num<mid){
l^=num;
}
else{
r^=num;
}
}
if(l!=0&&r!=0){
return new int[]{l,r};
}
else if(l==0){
lo=mid+1;
}
else{
hi=mid-1;
}
}
return null;
}
}
3.复杂度分析
- 时间复杂度:需要二分log(max-min)次,所以时间复杂度为O(nlog(max-min))。
- 空间复杂度:需要额外常数级别的内存空间,所以空间复杂度为O(1)。
剑指offer全集入口: 请戳这里
本文介绍了一种算法问题的解决方法,即在一个除两个数字外其他数字都出现了两次的整型数组中找出仅出现一次的两个数字。提供了三种方法:计数、异或分组和二分法,并详细分析了每种方法的实现过程及复杂度。
3262

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



