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

2024-05-09 12:36

本文主要是介绍每日一题——力扣面试题 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; // 初始化结果变量为0for (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];}}
}

数组中的两个元素求和:

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

这篇关于每日一题——力扣面试题 17.04. 消失的数字的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/973428

相关文章

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

usaco 1.2 Name That Number(数字字母转化)

巧妙的利用code[b[0]-'A'] 将字符ABC...Z转换为数字 需要注意的是重新开一个数组 c [ ] 存储字符串 应人为的在末尾附上 ‘ \ 0 ’ 详见代码: /*ID: who jayLANG: C++TASK: namenum*/#include<stdio.h>#include<string.h>int main(){FILE *fin = fopen (

荣耀嵌入式面试题及参考答案

在项目中是否有使用过实时操作系统? 在我参与的项目中,有使用过实时操作系统。实时操作系统(RTOS)在对时间要求严格的应用场景中具有重要作用。我曾参与的一个工业自动化控制项目就采用了实时操作系统。在这个项目中,需要对多个传感器的数据进行实时采集和处理,并根据采集到的数据及时控制执行机构的动作。实时操作系统能够提供确定性的响应时间,确保关键任务在规定的时间内完成。 使用实时操作系统的

一些其他面试题

阿里二面:那你来说说定时任务?单机、分布式、调度框架下的定时任务实现是怎么完成的?懵了。。_哔哩哔哩_bilibili 1.定时算法 累加,第二层每一个格子是第一层的总时间400 ms= 20 * 20ms 2.MQ消息丢失 阿里二面:高并发场景下引进消息队列有什么问题?如何保证消息只被消费一次?真是捏了一把汗。。_哔哩哔哩_bilibili 发送消息失败

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟)

【每日一题】LeetCode 2181.合并零之间的节点(链表、模拟) 题目描述 给定一个链表,链表中的每个节点代表一个整数。链表中的整数由 0 分隔开,表示不同的区间。链表的开始和结束节点的值都为 0。任务是将每两个相邻的 0 之间的所有节点合并成一个节点,新节点的值为原区间内所有节点值的和。合并后,需要移除所有的 0,并返回修改后的链表头节点。 思路分析 初始化:创建一个虚拟头节点

zookeeper相关面试题

zk的数据同步原理?zk的集群会出现脑裂的问题吗?zk的watch机制实现原理?zk是如何保证一致性的?zk的快速选举leader原理?zk的典型应用场景zk中一个客户端修改了数据之后,其他客户端能够马上获取到最新的数据吗?zk对事物的支持? 1. zk的数据同步原理? zk的数据同步过程中,通过以下三个参数来选择对应的数据同步方式 peerLastZxid:Learner服务器(Follo

每日一题|牛客竞赛|四舍五入|字符串+贪心+模拟

每日一题|四舍五入 四舍五入 心有猛虎,细嗅蔷薇。你好朋友,这里是锅巴的C\C++学习笔记,常言道,不积跬步无以至千里,希望有朝一日我们积累的滴水可以击穿顽石。 四舍五入 题目: 牛牛发明了一种新的四舍五入应用于整数,对个位四舍五入,规则如下 12345->12350 12399->12400 输入描述: 输入一个整数n(0<=n<=109 ) 输出描述: 输出一个整数

java常用面试题-基础知识分享

什么是Java? Java是一种高级编程语言,旨在提供跨平台的解决方案。它是一种面向对象的语言,具有简单、结构化、可移植、可靠、安全等特点。 Java的主要特点是什么? Java的主要特点包括: 简单性:Java的语法相对简单,易于学习和使用。面向对象:Java是一种完全面向对象的语言,支持封装、继承和多态。跨平台性:Java的程序可以在不同的操作系统上运行,称为"Write once,

每日一练7:简写单词(含链接)

1.链接 简写单词_牛客题霸_牛客网 2.题目 3.代码1(错误经验) #include <iostream>#include <string>using namespace std;int main() {string s;string ret;int count = 0;while(cin >> s)for(auto a : s){if(count == 0){if( a <=

两数之和--力扣1

两数之和 题目思路C++代码 题目 思路 根据题目要求,元素不能重复且不需要排序,我们这里使用哈希表unordered_map。注意题目说了只对应一种答案。 所以我们在循环中,使用目标值减去当前循环的nums[i],得到差值,如果我们在map中能够找到这个差值,就说明存在两个整数的和为目标值。 如果没有找到,就将当前循环的nums[i]以及下标i放入map中,以便后续查