每日一题——力扣面试题 17.04. 消失的数字

题目链接:https://leetcode.cn/problems/missing-number-lcci/description/

菜鸡做法:

#include <stdlib.h> // 包含标准库头文件,用于内存分配等功能


// 函数定义:寻找缺失的数字
int missingNumber(int* nums, int numsSize){
    // 使用calloc分配内存并初始化为0,大小为numsSize+1,因为缺失的数字在0到numsSize之间
    int *buffer = calloc(numsSize + 1, sizeof(int));
    // 遍历数组,标记出现过的数字
    for(int i = 0; i < numsSize; i++) {
        buffer[nums[i]] = 1; // 将出现过的数字对应的位置标记为1
    }
    // 再次遍历buffer数组,寻找未被标记的位置
    for(int i = 0; i < numsSize + 1; i++) {
        if(buffer[i] == 0) { // 如果某位置为0,说明该数字未出现过
            free(buffer); // 释放之前分配的内存
            return i; // 返回缺失的数字
        }
    }
    free(buffer); // 释放内存
    return -1; // 如果所有数字都出现过,按题意这种情况不会发生,返回-1作为错误标志
}

更好的做法:

数学:

我们知道,从0到n的整数之和可以用等差数列求和公式表示为 n * (n + 1) / 2。我们可以先计算出这个和,然后减去数组中所有数字的和,得到的结果就是缺失的数字。

下面是使用求和公式解决这个问题的代码实现,并加上了注释:

#include <stddef.h> // 包含标准库头文件,用于NULL等常量

// 函数定义:寻找缺失的数字
int missingNumber(int* nums, int numsSize) {
    // 计算从0到numsSize的整数之和
    int total = numsSize * (numsSize + 1) / 2;
    
    // 计算数组中所有数字的和
    int arraySum = 0;
    for (int i = 0; i < numsSize; i++) {
        arraySum += nums[i];
    }
    
    // 缺失的数字就是总和减去数组中数字的和
    int missing = total - arraySum;
    
    // 返回缺失的数字
    return missing;
}

在这个实现中,没有使用额外的内存空间,因此也没有内存释放的操作。这种方法的时间复杂度是O(n),空间复杂度是O(1),是一种比较高效的解决方案。

利用异或的性质:

使用位运算解决寻找缺失数字的问题,可以利用异或运算的性质:一个数和它本身进行异或运算结果为0,一个数和0进行异或运算结果为它本身。因此,可以将数组中的所有数字和0到n(n为数组长度)的所有数字进行异或运算,最终的结果就是缺失的数字。

下面是使用位运算解决这个问题的代码实现:

#include <stddef.h> // 包含标准库头文件,用于NULL等常量

// 函数定义:寻找缺失的数字
int missingNumber(int* nums, int numsSize) {
    int missing = numsSize; // 初始化缺失的数字为数组长度
    for (int i = 0; i < numsSize; i++) {
        missing ^= i ^ nums[i]; // 对数组中的数字和0到numsSize的数字进行异或运算
    }
    return missing; // 返回缺失的数字
}

在这个实现中,不需要分配额外的内存空间,因此也没有内存释放的操作。这种方法的时间复杂度是O(n),空间复杂度是O(1),比使用额外数组的方法更加高效。

这段代码实现了一个寻找数组中缺失数字的函数,它体现了以下哲学和编程思想:

  1. 对称性与互补性:在数学和哲学中,对称性和互补性是重要的概念。在这个算法中,通过异或运算(XOR)来寻找缺失的数字,利用了异或运算的对称性:任何数与自身异或结果为0,任何数与0异或结果为原数。这种对称性在算法中被用来消除成对的重复数字,从而揭示出缺失的数字。

  2. 简洁性:编程中的一个重要原则是KISS(Keep It Simple, Stupid),即保持代码的简洁性。这个算法通过一个简单的循环和异或运算就解决了问题,没有使用复杂的结构或额外的空间,体现了代码的简洁和高效。

  3. 数学思维:编程常常需要将问题抽象成数学模型来解决。在这个算法中,通过数学上的异或运算来处理数据,体现了数学思维在编程中的应用。

  4. 逻辑推理:算法的设计基于逻辑推理,即通过已知的数学性质(异或运算的性质)来推导出解决问题的方法。这种逻辑推理是编程解决问题的基础。

  5. 优化思想:在编程中,优化是一个重要的考虑因素。这个算法的时间复杂度是O(n),空间复杂度是O(1),是一种时间和空间上都高效的解决方案,体现了优化思想。

  6. 不变性:在函数式编程中,不变性是一个重要的概念,即数据一旦创建就不应该被修改。虽然这个算法不是函数式编程的例子,但它利用了异或运算的不变性来处理数据,即通过异或运算不会改变原始数据,而是产生新的结果。

  7. 抽象与封装:这个算法将寻找缺失数字的逻辑封装在一个函数中,对外部隐藏了实现细节,只提供了一个简单的接口。这种抽象和封装是良好软件设计的基础。

举一反三——异或能够解决哪些问题?:

位异或操作是一种非常有用的位操作,它可以解决多种编程和算法问题,特别是那些涉及成对出现的元素或需要在不使用额外空间的情况下操作数据的问题。以下是一些可以通过位异或操作解决的问题类型及示例:


找出唯一未成对的数字:

在一个数组中,每个元素都成对出现,只有一个元素是唯一的。使用异或可以找到这个唯一的元素,因为相同的数字异或结果为0,任何数字与0异或结果为其本身。

// 函数定义:寻找只出现一次的数字
int singleNumber(int* nums, int numsSize) {
    int result = 0; // 初始化结果变量为0
    for (int i = 0; i < numsSize; i++) {
        result ^= nums[i]; // 对数组中的每个元素进行异或运算
    }
    return result; // 返回结果,即只出现一次的数字
}

交换两个变量:不使用临时变量交换两个变量的值。

// 函数定义:交换两个整数的值
void swap(int *x, int *y) {
    if (*x != *y) { // 检查两个指针指向的值是否不同,避免相同的内存地址操作,导致结果归零
        // 通过异或运算交换两个数的值
        *x ^= *y; // 将x的值与y的值进行异或,结果保存回x
        *y ^= *x; // 将y的值与上一步的结果(x的新值)进行异或,得到x的原始值,保存回y
        *x ^= *y; // 将x的值与y的新值(x的原始值)进行异或,得到y的原始值,保存回x
    }
}

例子说明过程:

假设我们有两个整数a = 5b = 3,我们想要交换它们的值。我们可以通过调用swap(&a, &b)来实现。

  1. 初始状态:a = 5b = 3
  2. 执行*x ^= *y;a = a ^ b = 5 ^ 3 = 6
  3. 执行*y ^= *x;b = b ^ a = 3 ^ 6 = 5(此时b已经变成了a的原始值)。
  4. 执行*x ^= *y;a = a ^ b = 6 ^ 5 = 3(此时a已经变成了b的原始值)。

最终结果:a = 3b = 5,成功交换了两个数的值。

注意:如果xy指向的是同一个内存地址,即*x*y是同一个数,那么上述交换操作会导致该数变为0,因为任何数与自身异或的结果都是0。因此,函数中加入了if (*x != *y)的检查来避免这种情况。

找出两个唯一未成对的数字:

在一个数组中,除了两个数字是唯一的,其他都成对出现。可以通过异或分组的方式找到这两个唯一的数字。

void findTwoUniqueNumbers(int* nums, int numsSize, int* num1, int* num2) {
    int xor = 0;
    // 第一次遍历:对数组中所有元素执行异或操作。
    // 相同的数字会相互抵消,最终结果是两个唯一数字的异或结果。
    for (int i = 0; i < numsSize; i++) {
        xor ^= nums[i];
    }
    
    // 找到异或结果中任意一个为1的位,我们这里选择最右边的一个。
    // 这一位能够帮助我们区分两个唯一的数字。
    int rightmostSetBit = xor & ~(xor - 1); 
    
    *num1 = 0; // 初始化结果变量
    *num2 = 0; // 初始化结果变量
    
    // 第二次遍历:根据rightmostSetBit将数组分组,并分别异或。
    // 因为rightmostSetBit是两个唯一数字之间的不同位,所以它可以将这两个数字
    // 分到不同的组中,而成对出现的数字会被分到同一组并在异或中抵消。
    for (int i = 0; i < numsSize; i++) {
        if ((nums[i] & rightmostSetBit) != 0) {
            // 如果nums[i]在rightmostSetBit位上是1,说明它和其中一个唯一数字
            // 在该位上不同,因此将其与num1进行异或运算。
            *num1 ^= nums[i];
        } else {
            // 否则,说明它和另一个唯一数字在该位上不同,将其与num2进行异或运算。
            *num2 ^= nums[i];
        }
    }
}

数组中的两个元素求和:

虽然异或操作本身不直接用于求和,但在某些特定的位操作问题中,如求两个非负整数的和而不使用加号或其他算术运算符时,可以用异或来模拟加法操作中的“不进位求和”,同时用与操作和左移操作来模拟进位操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/605391.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从离线到实时:无锡锡商银行基于 Apache Doris 的数据仓库演进实践

作者&#xff1a;武基鹏&#xff0c;无锡锡商银行 大数据技术经理 编辑整理&#xff1a;SelectDB 技术团队 导读&#xff1a;为实现数据资产的价值转化以及全面数字化、智能化的风险管理&#xff0c;无锡锡商银行大数据平台经历从 Hive 离线数据仓库到 Apache Doris 实时数据仓…

Hive SQL-DQL-Select查询语句用法详解

HQL Select用法详解 1.基础语法 &#xff08;1&#xff09;select_exp &#xff08;2&#xff09;ALL、DISTINCT &#xff08;3&#xff09;WHERE &#xff08;4&#xff09;分区查询、分区裁剪 &#xff08;5&#xff09;GROUP BY &#xff08;6&#xff09;HAVING &#xff0…

hadoop学习---基于Hive的教育平台数据仓库分析案例(三)

衔接第一部分&#xff0c;第一部分请点击&#xff1a;基于Hive的教育平台数据仓库分析案例&#xff08;一) 衔接第二部分&#xff0c;第二部分请点击&#xff1a;基于Hive的教育平台数据仓库分析案例&#xff08;二) 学生出勤模块&#xff08;全量分析&#xff09;&#xff1a…

Densenet+SE

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊# 前言 前言 这周开始学习关于经典模型的改进如加注意力机制&#xff0c;这周学习Densenet加通道注意力即SE注意力机制。 ##SE注意力机制简介 SE&#xff08;…

自定义shell

1、首先我们的程序要打印出命令行 命令行》用户名【主机名】当前路劲$:命令字符串 用户名、主机名、当前路径可以通过系统调用函数getenv()得到&#xff1a; 2、获取命令字符串 把输入的命令字符串放到一个指针数组中 但是我们发现用scanf函数输入的话&#xff0c;遇到空…

【数据结构】-- 链表专题

链表的分类 前面我们实现了单链表&#xff0c;单链表只是链表的一种。可以根据以下几个标准来判断链表的类型&#xff1a; 1.单向或者双向 如图所示&#xff0c;单向链表中一个节点的指针域只储存了下一个节点的指针&#xff0c;能通过前一个节点访问后一个节点&#xff0c;无…

Vue 3.3 编译宏 vue3.3新增了一些语法糖和宏,包括泛型组件、defineSlots、defineEmits、defineOptions

Vue 3.3新增了一些语法糖和宏&#xff0c;包括泛型组件、defineSlots、defineEmits、defineOptions defineProps 父组件传参 <template><Child name"my"></Child> </template> <script setup lang"ts"> import Child fro…

使用Docker安装Yapi接口管理工具

简介&#xff1a; YAPI 是由去哪儿网移动架构组开发的一款可视化接口管理工具。它具有可视化管理、高效易用、功能强大等特点。它提供了便捷的接口创建、发布和维护方式&#xff0c;开发人员可以通过简单的操作实现接口管理。 YAPI 还支持类似 postman 的接口调试&#xff0c;对…

06-数组

1. 为什么需要数组 一个养鸡场有6只鸡&#xff0c;它们的体重分别是3kg&#xff0c;4kg&#xff0c;1kg&#xff0c;2kg&#xff0c;6kg&#xff0c;3kg。 没有数组&#xff0c;就需要定义六个变量&#xff0c;一个变量代表一只鸡的体重。 使用数组&#xff0c;就可以定义一…

TypeScript学习日志-第二十三天(装饰器Decorator)

装饰器Decorator 一、类装饰器 ClassDecorator 其中返回的 target 是 Http 的构造函数&#xff0c;有了构造函数就不会去破坏其自身原有的结构&#xff0c;当我们 Http 里面有多个属性或者方法的&#xff0c;当是我们不想看或者改变它&#xff0c;这时候可以在构造函数中增加即…

【Mybatis操作数据库】入门(一)

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【MyBatis框架】 本专栏旨在分享MyBatis框架的学习笔记&#xff0c;如有错误定当洗耳恭听&#xff0c;欢迎大家在评论区交流讨论&#x1f…

59岁前TVB男拳王内地登台疑黑面 被批耍大牌

现年59岁的郭政鸿在2015年离巢TVB后转往内地发展&#xff0c;密密拍剧、登台及直播带货&#xff0c;短短几年就已经储够钱&#xff0c;斥资过千万买楼&#xff0c;成功上车做业主&#xff0c;可见收入丰厚。 早前郭政鸿现身顺德&#xff0c;在酒吧登台唱歌&#xff0c;有网民上…

《铁路出行更便捷:火车票预定审批系统的设计与应用》

在现代化的铁路交通管理中&#xff0c;火车票预定审批系统扮演着至关重要的角色。它不仅能够有效管理员工出差、培训等需要乘坐火车的行程&#xff0c;还能够提高审批效率&#xff0c;减少人力成本&#xff0c;确保出行安全。本文将探讨火车票预定审批系统的设计原则和应用场景…

计算机毕业设计Python+Spark知识图谱医生推荐系统 医生门诊预测系统 医生数据分析 医生可视化 医疗数据分析 医生爬虫 大数据毕业设计 机器学习

摘 要 随着我国社会经济发展水平的不断提高&#xff0c;人们的物质生活水平也有了很大的改善&#xff0c;越来越多的人不满足于当前的医疗服务质量&#xff0c;由于地域和空间的限制&#xff0c;医疗资源不平衡&#xff0c;无法实现全民共享。针对当今社会中存在的求医难的问题…

智能家居2 -- 实现网络控制模块

这一模块的思路和前面的语言控制模块很相似&#xff0c;差别只是调用TCP 去控制 废话少说&#xff0c;放码过来 增添/修改代码 socket_interface.c #include <pthread.h>#include "socket_interface.h" #include "control.h" #include "socke…

【教程】超简单!如何将“在VSCode中打开”添加到右键菜单中

按照以下步骤进行操作&#xff1a; 打开注册表编辑器&#xff1a; 按下 Win R 组合键打开运行对话框。输入 regedit 并按下 Enter 键打开注册表编辑器。 导航到适当的注册表项&#xff1a; 转到以下注册表项&#xff1a;HKEY_CLASSES_ROOT\Directory\Background\shell 创建…

26版SPSS操作教程(高级教程第十九章)

目录 前言 粉丝及官方意见说明 第十九章一些学习笔记 第十九章一些操作方法 树模型、随机森林与最近邻元素法 树模型 数据准备 具体操作 结果解释 对案例的进一步分析 结果解释 考虑应用模型时的成本与收益 保存新数据 在选项中看错误分类成本和利润 结果解释…

【管理篇】如何管理情绪?

目录标题 为什么要特别关注激动和愤怒两种情绪呢&#xff1f;管理自己的情绪大致的步骤三层脑结构爬行脑情绪脑视觉脑 大家说的情绪管理&#xff0c;基本上都是对于情绪激动、生气甚至是愤怒的管理&#xff1b;日常所说的情绪化&#xff0c;一般也是指某个人特别容易情绪激动&a…

Gitlab自动化测试的配置

1. 代码分支命名规范检测 Setting → Repository → Push rules → Branch name&#xff0c;添加分支命名规范对应的正则表达式。如&#xff1a; ^(Release|Tag|Develop|Feature)_._.|Main$ 表示分支名只能以以下关键字之一开头&#xff1a;Release、Tag、Develop和Feature。 …

基于模糊控制的AMT自动变速汽车换档智能控制系统simulink建模与仿真

目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 基于模糊控制的AMT自动变速汽车换档智能控制系统simulink建模与仿真。 2.系统仿真结果 输入的V&#xff0c;Ac&#xff0c;a 输出的档位&#xff1a; 3.核心程序与模型 版…