C语言练习:(力扣645)错误的集合

题目链接:645. 错误的集合 - 力扣(LeetCode)

集合 s 包含从 1n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复
给定一个数组 nums 代表了集合 S 发生错误后的结果。
请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。

思路解析:

本题可以考虑两个思路:

  • 数学方法

先对数组进行由小到大排序,因为缺失的数值介于前一个数值和后一个数值中间,并且在不缺失的情况下相邻两个数值差值为1,如果有数值重复,那么重复的数值和下一个不同的数值之间的差值为2,所以需要记录前一个数值存储在变量prev
📌
注意prev初始化为0,因为存在数组中缺失的数值为1,而如果数组中只有两个元素,那么最大值为2,此时在数组中找不到一个数值使得重复的数值和下一个的不同两个数值差值为2

先找到重复的数值,即下一个元素和当前元素cur相同时,存储当前数值cur到新数组的第一个元素的位置,更新prev为当前的重复数值

再继续循环,若当前数值与prev中存的数值差值大于1,说明此时已经找完了重复元素,因为不缺失数值时相邻两个数值差值为1,而此时prev中的数值和当前数值差值大于1,说明prev中的数值+1即为缺失的数值,将prev + 1存储新数组的第二个位置后更新prev

当走完该循环时,还需要注意一个问题:如果缺失的数值是数组的最大值(例如122,缺失3),此时需要单独判断,因为不存在一个数值使得后一个元素和当前元素差值为2,所以当数组的最后一个元素不等于数组的大小时,此时即为缺失数组最大值

  • 异或运算

本题同样可以考虑异或运算对重复数值和缺失数值分堆来求解,但是直接在原数组中对元素进行异或不可取

考虑到以下规律:

在原数组中,观察到重复的数值和缺失的数值都出现偶数次,而其他数值均出现奇数次,如果构造一个1~n的不缺数的数组和原数组组合,那么重复的数值和缺失的数值都出现奇数次,而其他数值均出现偶数次,在异或运算中,相同的两个数异或可以得到a^a = 0,而a^0 = a,故此时对构造的数组和原数组进行整体异或可以得到重复的数值和缺失的数值异或的结果值

故采用先构造一个数组和原数组进行整体异或,得到一个返回值ret即为重复数值和缺失数值的异或结果,因为异或运算的本质是找出两个操作数二进制位不同的位置,所以计算结果的二进制位为1的位置即为重复的数值和缺失的数值相异的位置,此时可以采用循环结合右移运算符找到相异的位置(二进制位数的下标,例如1和5中倒数第三位不同),但是本次将采用返回值与返回值的负数形式相与计算得出最低的不同位,即int position = ret & (-ret);,但是注意该语句计算的不是不同的下标位置,而是具体的值,但是该值的二进制值中为1的位置为对应的两个数不同的位置

找到最低的不同位之后,通过原数组和构造的数组中的元素与不同位的数值相与运算得到结果为0和不为0的数值,将二者分堆即可分出重复的数值和缺失的数值(因为重复的数值和缺失的数值不相同,所以二者不可能在同一堆中)

最后,因为暂时不知道重复的数值和缺失的数值具体所在的位置,所以需要遍历原数组确定重复的数值存储在新数组的第一个元素的位置,缺失的数值则放置到新数组的第二个位置

参考答案:

排序+调整(数学方法)

cpp 复制代码
/*
 * @lc app=leetcode.cn id=645 lang=c
 *
 * [645] 错误的集合
 */

// @lc code=start
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

int cmp(const void *p1, const void *p2)
{
    return (*(int *)p1 - *(int *)p2);
}
int *findErrorNums(int *nums, int numsSize, int *returnSize)
{
    // 对已知数组进行从小到大排序
    qsort(nums, numsSize, sizeof(int), cmp);
    int *p = (int *)malloc(sizeof(int) * 2);

    // 定义变量记录数组中上一个元素的位置,便于计算缺失的数值
    int prev = 0;
    // 遍历数组
    // 1.如果找出的元素与prev中的值差值大于1,说明当前缺失的元素即为prev当前值的后一位数值
    // 2.如果找出的元素与prev中的值相等,说明遇到了相同元素
    for (int i = 0; i < numsSize; i++)
    {
        int cur = nums[i]; // 遍历数组
        if (cur == prev)
        {
            // 如果相同则表示遇到相同元素
            p[0] = cur; // 记录相同元素
        }
        else if (cur - prev > 1)
        {
            // 如果不同且满足二者差值大于1时,则表示中间有缺失元素,并且缺失元素即为prev当前大小+1
            p[1] = prev + 1;
        }
        prev = cur; // 更新prev
    }
    // 存在一种情况:数组中最后一个数值小于数组长度
    if (nums[numsSize - 1] != numsSize)
    {
        // 数组中缺失的数值为数组的最大值
        p[1] = numsSize;
    }
    *returnSize = 2;
    return p;
}
// @lc code=end

异或运算

cpp 复制代码
/*
 * @lc app=leetcode.cn id=645 lang=c
 *
 * [645] 错误的集合
 */

// @lc code=start
/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

int *findErrorNums(int *nums, int numsSize, int *returnSize)
{
    int *p = (int *)malloc(sizeof(int) * 2);
    int ret = 0;
    // 构造一个1-n的不缺数值的数组与原来的数组进行整体异或
    for (int i = 1; i <= numsSize; i++)
    {
        // 将不缺数的数组进行整体异或
        ret ^= i;
        // 将缺数的数组与不缺数的数组进行整体异或
        ret ^= nums[i - 1];
    }

    // 当前ret中存储的值为重复数值和缺失数值异或的结果
    // 找出重复数值和缺失数值二者最低的不同位
    // 可以代替原来的移位找最低不同位算法
    int position = ret & (-ret);
    // 定义两个数值分别存储重复数值和缺失的数值
    int num1 = 0;
    int num2 = 0;
    // 根据最低的不同二进制位对原数组进行分组
    for (int i = 0; i < numsSize; i++)
    {
        if ((nums[i] & position) == 0)
        {
            num1 ^= nums[i];
        }
        else
        {
            num2 ^= nums[i];
        }
    }
    // 根据最低的不同二进制位对构造数组进行分组
    for (int i = 1; i <= numsSize; i++)
    {
        if ((i & position) == 0)
        {
            num1 ^= i;
        }
        else
        {
            num2 ^= i;
        }
    }

    // 因为当前不确定重复的数值和缺失的数值在num1和还是num2,所以需要与原数组进行再一次的比较找出重复数值,剩下的就是缺失的数值
    *returnSize = 2;
    for (int i = 0; i < numsSize; i++)
    {
        if (nums[i] == num1)
        {
            p[0] = num1;
            p[1] = num2;
            return p;
        }
    }

    // 如果已经确定则直接返回
    p[0] = num2;
    p[1] = num1;
    return p;
}
// @lc code=end
相关推荐
长天一色11 分钟前
C语言日志类库 zlog 使用指南(第五章 配置文件)
c语言·开发语言
whltaoin19 分钟前
【408计算机考研课程】-C语言认知
c语言·考研
一般清意味……23 分钟前
快速上手C语言【上】(非常详细!!!)
c语言·开发语言
自由的dream24 分钟前
0-1背包问题
算法
卑微求AC24 分钟前
(C语言贪吃蛇)16.贪吃蛇食物位置随机(完结撒花)
linux·c语言·开发语言·嵌入式·c语言贪吃蛇
2401_8572979130 分钟前
招联金融2025校招内推
java·前端·算法·金融·求职招聘
良月澪二2 小时前
CSP-S 2021 T1廊桥分配
算法·图论
卑微求AC3 小时前
(C语言贪吃蛇)11.贪吃蛇方向移动和刷新界面一起实现面临的问题
c语言·开发语言
wangyue43 小时前
c# 线性回归和多项式拟合
算法
&梧桐树夏3 小时前
【算法系列-链表】删除链表的倒数第N个结点
数据结构·算法·链表