C算法--数组中消失的数字和单身狗问题-异或
作者:互联网
第一:消失的数字
对于一个int数组其特点为:
1. 其元素个数为n
2. 每个元素的值为[0,n]之间(前闭后闭区间)
3. 元素的值没有重复,即0-n,这n+1个数字中的n个数字组成的数组
1.1 问题
在时间复杂度为O(n)和空间复杂度为O(1)的条件下,找到缺失的数字
1.2 解法
利用异或的特点:
1. 0 ^ A = A
2. 0 ^ A ^ A = 0
将0 与数组中每个元素以及0-n分别进行异或计算 ;
由于性质2,可得结果 = 0 ^ 缺失数字 = 缺失数字(性质1)
1.3 代码实现
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int miss_number(int* arr, int len)
{
// 利用异或算法进行求解
// 异或的特点
// 1. 异或计算的数对应的补码的二进制的异或,相同则为0,相异则为0
// 2. 两个相同数的异或为0
// 3. 0与任意正数(A)的异或均为任意正数其本身, 即0 ^ A = A
// 4. 0与任意正数的异或两次,结果为0,即0 ^ A ^ A = 0;
// 5. 且异或满足交换律,即 A ^ B ^ C = A ^ C ^ B = C ^ A ^ B
// 因此,当0与数组中的每个元素均进行异或,在于[0,len]之间的整数分别异或
// 即0余2*len - 1个数进行异或,由于只有缺失的数字出现一起,其余的均出现两次
// 根据性质4,结果为0 ^ 缺失的数字 = 缺失的数字
int ret = 0;
int i;
for (i = 0; i < len; i++)
{
ret ^= arr[i];
}
for (i = 0; i <= len; i++)
{
ret ^= i;
}
return ret;
}
int main()
{
// 用于查找的数组
int arr[] = {9,8,4,2,3,5,7,0,1};
// 获得数组的长度
int len = sizeof(arr) / sizeof(arr[0]);
// 查找并返回数组中缺失的数字
int ret = miss_number(arr, len);
printf("the miss number is %d\n", ret);
return 0;
}
1.4 结果展示
第二:找单身狗问题
数组的特点
1. 数组的元素个数为2n + 1个
2. 其中n个数字出现两次
3. 1个数字出现1次
2.1 问题
找出只出现一次的那个数字
解法:对所有的元素进行异或操作,其结果为只出现一次的的那个数字。
因为两个相同数字的异或结果为0.
0余任意数的异或结果为其自身
2.2 代码部分
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int fine_single(int* arr, int len)
{
int i;
int ret = 0;
for (i = 0; i < len; i++)
{
ret ^= arr[i];
}
return ret;
}
int main()
{
// 用于查找的数组
int arr[] = {9,8,9,0,1,5,8,0,1};
// 获得数组的长度
int len = sizeof(arr) / sizeof(arr[0]);
// 查找并返回数组中单独的数字
int ret = fine_single(arr, len);
printf("the single number is %d\n", ret);
return 0;
}
2.3 结果
第三:两个单身狗
数组描述:
1. 数组的长度为2*n
2. 数组中有n-1的元素出现两次,或4次,或2k次,k > 0
3. 数组中有2个元素值出现一次
3.1 问题
找出该数组中只出现一次的那两个数字。
要求:时间和空间复杂度分别为O(n)和O(1)
3.2 解法
1. 对数组中所有的元素进行异或操作,根据异或的性质(A ^ A = 0)得到其结果为两个出现一次数字的异或结果ret。
2. 对数组中的元素分为两组
2.1 按照ret中位为1的任意一个位对数组进行分组。
2.2 将该位的值为1的元素分到一组,为1的元素分到另外一组。
3. 分组的结果:
3.1 将两个出现一次的数字分到不同的组,因为他们在该位的异或结果为1,故他们在该位的值为0和1,故将他们分到不同的组。
3.2 将出现2次的数字分到相同的组,因为相同的数字,在该位的值相同(0或1),过分组时,分到同一组。
3.3 每组的元素中个包含一个只出现一次的数字,其余数字均出现两次。
4. 转化为单身狗的问题。
3.3 代码实现1
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
void fine_double_single(int* arr, int len, int* sig)
{
int i;
int ret = 0; // 保存每个元素与0的异或的结果
int oa = 1; // 找到ret中位为1的位,用于按位与进行分组
int idx = 0; // 用于记录分组的元素个数
int index = 0; // 用于记录组的索引
int oav; // 用于分组
int* arr1 = (int*)malloc(sizeof(int) * len); // 动态开辟内存空间,用于存储分组的元素
if (NULL == arr1) { return; }
// 对所有元素进行异或处理,得到两个只出现一次的数字的异或的结果
for (i = 0; i < len; i++)
{
ret ^= arr[i];
}
// 找到一个ret中位为1的位,让该位为1,其余位为0的数
for (i = 0; i < sizeof(int) * 8; i++)
{
if (0 != (ret & oa))
break;
oa <<= 1;
}
for (index = 0; index < 2; index++)
{
idx = 0;
if (0 == index)
oav = 0;
else
oav = oa;
for (i = 0; i < len; i++)
{
if (oav == (arr[i] & oa))
{
arr1[idx] = arr[i];
idx++;
}
}
ret = 0;
for (i = 0; i < idx; i++)
{
ret ^= arr1[i];
}
sig[index] = ret;
}
free(arr1);
arr1 = NULL;
}
int main()
{
int sig[2] = { 0 };
// 用于查找的数组
int arr[] = { 9, 8, 9, 0, 1, 5, 8, 0, 1, 7 };
// 获得数组的长度
int len = sizeof(arr) / sizeof(arr[0]);
// 查找并返回数组中两个单独的数字
fine_double_single(arr, len, sig);
printf("the double single number is %d and %d\n", sig[0], sig[1]);
return 0;
}
3.4 代码实现2(简洁不用动态分配内存)
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
fine_double_single(int* nums, int numSize, int* retSigs)
{
// 假设两个单独出现的数字分别为x1和x2;
int x1, x2;
// 第一步用0对数组中的所有元素进行异或操作
// 因为0 ^ A = A and A ^ A = 0
// 因此得到的结果为两个单独出现数字的异或结果,即ret = x1 ^ x2
int ret = 0;
int i;
for (i = 0; i < numSize; i++)
{
ret ^= nums[i];
}
// 第二步 找到ret比特位中任意一个为1的位(M),因为位相同时,异或为0,不同时,异或为1.
// 这样根据M位为1,其余位为0的值进行按位与计算 可以把x1和x2分到不同的组中
// 从最低位开始搜索,如果按位与为0,则进行左移操作,直到找到为1的位,这样按位与结果不为0
int m = 0;
for (m = 0; m < sizeof(int) * 8; m++)
if (ret & (1<<m)) break;
// 进行计算,由于分组,需要分配空间来存储指定的元素,比较麻烦
// 因此将依次得到的指定组的元素直接进行计算,即直接与0依次进行异或操作
// 因为分组后,接下来要做的无非就是让每个元素与0分别进行异或操作
// 因此设x1和x2均为0
x1 = 0;
x2 = 0;
for (i = 0; i < numSize; i++)
{
if (nums[i] & (1 << m))
{
x1 ^= nums[i];
}
else
{
x2 ^= nums[i];
}
}
// 将结果保存到retSigs数组中
retSigs[0] = x1;
retSigs[1] = x2;
}
int main()
{
int sig[2] = { 0 };
// 用于查找的数组
int arr[] = { 9, 8, 9, 0, 1, 5, 8, 0, 1, 7 };
// 获得数组的长度
int len = sizeof(arr) / sizeof(arr[0]);
// 查找并返回数组中两个单独的数字
fine_double_single(arr, len, sig);
printf("the double single number is %d and %d\n", sig[0], sig[1]);
return 0;
}
标签:数字,--,ret,int,异或,len,单身,数组 来源: https://blog.51cto.com/u_15132389/2847665