0
点赞
收藏
分享

微信扫一扫

【leetcode】剑指 Offer 56 - I. 数组中数字出现的次数(C语言)



文章目录

  • ​​题目说明​​
  • ​​方法1:常规做法​​
  • ​​方法2:异或求解​​
  • ​​复习一下位操作符​​
  • ​​题解​​
  • ​​案例1​​
  • ​​案例2​​
  • ​​敲代码​​
  • ​​验证​​


题目说明



难度:中等

一个整型数组 ​​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]

限制:​​2 <= nums.length <= 10000​

方法1:常规做法

最容易想到的常规做法:

  • 先对该数组进行qsort排序,再用for循环遍历,找到数组中下标​​i​​​和​​i+1​​不相等的哪一项,这一项i即为只出现了一次的数字。

  • 若最末尾的数是目标数,需要进行判断并break,防止越界访问数组


如果你不了解qsort,可以看这篇博客学习一下????​​点我​​


//方法1,太过简单,不够高级
cmp(const void* e1, const void* e2)
{
return *(int*)e1 - *(int*)e2;
}

int* singleNumbers(int* nums, int numsSize, int* returnSize) {
int* result = (int*)calloc(2, sizeof(int));
int k = 0;

qsort(nums, numsSize, sizeof(int), cmp);//对原数组进行排序

int i = 0;
for (i = 0; i < numsSize; i++)
{
if (i == numsSize-1)//如果i是末尾数,需要进行判断
{
result[k] = nums[i];
break;
}

if (nums[i] == nums[i + 1])
{
i ++;
}
else
{
result[k] = nums[i];
k++;
}
}


*returnSize = 2;
return result;
}

【leetcode】剑指 Offer 56 - I. 数组中数字出现的次数(C语言)_操作符

方法2:异或求解

复习一下位操作符

很尬尴的是,本人对位操作符和移位操作符并不熟悉。经常把它们记混。几次测试的时候,涉及到这两个操作符的题目就是瞎蒙。

后来发现位操作符和移位操作符似乎是测试中经常考察的对象,​这里建议大家一定要把它们弄懂!

语句

操作符

对应结果

c = a & b

&按位与

全1为1,否则0

c = a | b

| 按位或

有1为1,否则0

c = a ^ b

^按位异或

相同为0,不同为1

题解

异或有一个特点,​​a^a=0​​​,​​a^0=a​

【leetcode】剑指 Offer 56 - I. 数组中数字出现的次数(C语言)_leetcode_02

案例1

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

以题目所给参考2为例

1^2^10^4^1^4^3^3 = 2^10

可以看到,当我们把所给数据全部异或在一起的时候,​其结果正好是我们需要的两个数相异或。

但是我们好像并没有什么好的办法把已经异或了的两个数拆开

这时候就需要对待查找的数据进行分组

  • 分组的目的:让两个单身狗分开到两组数据中
  • 这两组数据,除两个单身狗外,其他数字都出现了两次
1 1 3 3 4 4 2 
10

为什么是这么分的呢?

10: 1010
2: 0010
10^2=1000

可见单身狗10和2之间,源码第四位才有差别

1: 0001
2: 0010
3: 0011
4: 0100
10:1010

这时候我们就可以通过第4位的差距,将数据分为两组

  • 第一组是​​1~4​​,它们的第4位都是0

  • 第二组是​​10​​,只有它的第四位是1

再根据前面异或的特点,对这两组数字进行异或操作,即可得到单生狗10和2

1^1^3^3^4^4^2 = 2
10 = 10

案例2

上面这组数据有些特殊,第二组里面只有10一个数字

我们再来看一组数据

1 2 3 4 5 1 2 3 4 6

这一组数据中,单身狗是5和6

5:0101
6:0110
5^6 = 0011

可见5和6的源码中,第一、二位都有区别

这里我们取第一位来分类即可

1:0001
2:0010
3:0011
4:0100
5:0101
6:0110

二进制第一位为1的数,放入一组

1 1 3 3 5
2 2 4 4 6

对它们进行异或操作,就可以得到单身狗5和6

敲代码

//方法2-异或
int* singleNumbers(int* nums, int numsSize, int* returnSize) {
//1.全部异或在一起
int i = 0;
int k = 0;
for (i = 0; i < numsSize; i++)
{
k ^= nums[i];
//4^1^4^6 = 1^6
}
//2.判断k的二进制第几位是1
int set = 0;
for (i = 0; i < 32; i++)
{
if (((k >> i) & 1) == 1)//注意操作符优先级
{
set = i;//第i位为1
break;
}
}
//3.以第set位为1进行分组,可以将这两个数分开
int n = 0;
int m = 0;
for (i = 0; i < numsSize; i++)
{
if (((nums[i] >> set) & 1) == 1)
{
n ^= nums[i];
}
else
{
m ^= nums[i];
}
}

int* result = (int*)calloc(2, sizeof(int));
//返回的数组必须用动态内存管理来创建
result[0] = n;
result[1] = m;
*returnSize = 2;
return result;
}

验证

自己整一个main函数来测试一下代码是否能达到要求,​成功!


做接口题的时候,如果出现输出错误的情况,可以自己写一个main函数并调试来找出错误


【leetcode】剑指 Offer 56 - I. 数组中数字出现的次数(C语言)_数据_03

可以看到,运行的用时远远小于​方法1​的用时

击败了92.9%的用户!

【leetcode】剑指 Offer 56 - I. 数组中数字出现的次数(C语言)_操作符_04

你学会了吗????


如果这篇博客对你有帮助,点个????再走呗~




举报

相关推荐

0 条评论