【经典算法】LeetCode1:两数之和(Java/C/Python3实现含注释说明,Easy)

两数之和

  • 题目
  • 思路及实现
    • 方式一:暴力解法(不推荐)
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
    • 方式二:哈希表(推荐)
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
    • 方式三:双指针法
      • 思路
      • 代码实现
        • Java版本
        • C语言版本
        • Python3版本
      • 复杂度分析
  • 总结
  • 相似题目

  • 标签:哈希表、查找

题目

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。示例 1:输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:输入:nums = [3,3], target = 6
输出:[0,1]
提示:2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案

原题:LeetCode 1

思路及实现

方式一:暴力解法(不推荐)

思路

最容易想到的方法是枚举数组中的每一个数 x,寻找数组中是否存在 target - x。
当我们使用遍历整个数组的方式寻找 target - x 时,需要注意到每一个位于 x 之前的元素都已经和 x 匹配过,因此不需要再进行匹配。而每一个元素不能被使用两次,所以我们只需要在 x 后面的元素中寻找 target - x。

代码实现

Java版本

public int[] twoSum(int[] nums, int target) {int n = nums.length;for (int i = 0; i < nums.length; i++) { // 遍历数组,从第一个元素开始for (int j = i + 1; j < nums.length; j++) { // 在当前元素后面的元素中查找与目标值相加等于target的元素if (nums[i] + nums[j] == target) { // 如果找到了符合条件的元素对return new int[]{i, j}; // 返回这两个元素的下标}}}return new int[0]; // 如果没有找到符合条件的元素对,则返回空数组
}
C语言版本
#include <stdio.h>
#include <stdlib.h>int* twoSum(int* nums, int numsSize, int target, int* returnSize) {int* result = (int*)malloc(2 * sizeof(int)); // 分配保存结果的内存空间*returnSize = 0; // 初始化返回结果数组的大小为0,表示没有找到满足条件的元素对for (int i = 0; i < numsSize; i++) { // 外层循环遍历数组中的每个元素for (int j = i + 1; j < numsSize; j++) { // 内层循环遍历当前元素后面的每个元素if (nums[i] + nums[j] == target) { // 检查两个元素的和是否等于目标值result[0] = i; // 将符合条件的第一个元素的下标存入结果数组的第一个位置result[1] = j; // 将符合条件的第二个元素的下标存入结果数组的第二个位置*returnSize = 2; // 更新返回结果数组的大小为2return result; // 返回结果数组}}}return result; // 返回结果数组,如果没有找到满足条件的元素对,数组中的元素值均为0// 注意:需要在适当的时候释放result指向的动态分配内存,以避免内存泄漏
}
Python3版本
from typing import List
def twoSum(nums: List[int], target: int) -> List[int]:result = [] # 用于存储结果的列表n = len(nums)for i in range(n): # 外层循环遍历列表中的每个元素for j in range(i+1, n): # 内层循环遍历当前元素后面的每个元素if nums[i] + nums[j] == target: # 检查两个元素的和是否等于目标值result.append(i) # 将符合条件的第一个元素的下标添加到结果列表中result.append(j) # 将符合条件的第二个元素的下标添加到结果列表中return result # 返回结果列表return result # 如果没有找到满足条件的元素对,返回空列表

复杂度分析

  • 时间复杂度分析:O(n^2),其中n为数组nums的长度。这是由于代码使用了两层循环来遍历数组。外层循环将执行n次,而内层循环则将执行(n-1)次、(n-2)次、…、2次、1次,总的执行次数为n * (n-1) / 2,即O(n^2)。

  • 空间复杂度分析:O(1),即常数级别的空间复杂度。因为代码只使用了常数个额外变量来存储元素的下标和存储结果的数组。

方式二:哈希表(推荐)

思路

注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。

使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N) 降低到 O(1)。

这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。

代码实现

Java版本
import java.util.HashMap;class Solution {public int[] twoSum(int[] nums, int target) {//key为当前值,value为当前值的位置HashMap<Integer, Integer> map = new HashMap<>();for (int i = 0; i < nums.length; i++) {int complement = target - nums[i]; // 计算差值,即目标值与当前元素的差值if (map.containsKey(complement)) {return new int[]{map.get(complement), i}; // 返回HashMap中保存的差值元素的下标和当前元素的下标}map.put(nums[i], i); // 将当前元素添加到HashMap中}return new int[0]; // 如果没有找到满足条件的元素对,返回空数组}
}

说明:

C语言版本
#include <stdio.h>
#include <stdlib.h>int* twoSum(int* nums, int numsSize, int target, int* returnSize) {int* result = (int*)malloc(2 * sizeof(int));*returnSize = 0;// 创建哈希表int hashtable[20001] = {0};for (int i = 0; i < numsSize; ++i) {int complement = target - nums[i]; // 计算差值,即目标值与当前元素的差值// 检查哈希表中是否存在差值if (complement >= -10000 && complement <= 10000 && hashtable[complement + 10000] != 0) {result[0] = hashtable[complement + 10000] - 1; // 返回哈希表中保存的差值元素的下标result[1] = i; // 返回当前元素的下标*returnSize = 2; // 更新返回结果数组的大小为2return result;}// 将当前元素添加到哈希表中hashtable[nums[i] + 10000] = i + 1;}return result;
}
Python3版本
from typing import List
from collections import defaultdictclass Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:hashtable = defaultdict(int) # 使用defaultdict来容纳哈希表for i in range(len(nums)):complement = target - nums[i] # 计算差值,即目标值与当前元素的差值if complement in hashtable:return [hashtable[complement], i] # 返回哈希表中保存的差值元素的下标和当前元素的下标hashtable[nums[i]] = i # 将当前元素添加到哈希表中return [] # 如果没有找到满足条件的元素对,返回空列表

复杂度分析

  • 时间复杂度:O(N),其中 N 是数组中的元素数量。对于每一个元素 x,我们可以 O(1) 地寻找 target - x。
  • 空间复杂度:O(N),其中 N 是数组中的元素数量。主要为哈希表的开销。

方式三:双指针法

思路

双指针法是解决两数之和问题的一种常见方法。其思路是首先对输入数组进行排序,然后使用两个指针指向数组的起始位置和结束位置。在每一步迭代中,计算两个指针对应元素的和,并与目标值进行比较。

如果和等于目标值,说明找到了满足条件的两个数,返回它们的索引。
如果和小于目标值,说明需要增加和,因此将左指针向右移动一位。
如果和大于目标值,说明需要减少和,因此将右指针向左移动一位。
重复上述步骤直到找到满足条件的两个数或者左右指针相遇。

双指针法的优点是时间复杂度相对较低,只需要对数组进行一次排序,并且在有序数组中移动指针来寻找目标值,从而减少了比较次数。同时,该方法只需要常数级的额外空间。然而,需要注意的是双指针法要求输入数组必须是有序的,这会导致对原始数组的修改。

实现时可以使用Java的Arrays工具类对数组进行排序,然后使用左右指针按照上述思路进行迭代,最终找到满足条件的两个数或者返回空数组表示未找到。

代码实现

Java版本
import java.util.Arrays;public class TwoSum {public int[] twoSum(int[] nums, int target) {// 对数组进行排序Arrays.sort(nums);// 初始化左右指针int left = 0;int right = nums.length - 1;while (left < right) {// 计算当前左右指针对应元素之和int sum = nums[left] + nums[right];if (sum == target) {// 找到目标值,返回对应的数组下标return new int[]{left, right};} else if (sum < target) {// sum小于目标值,将左指针向右移动一位left++;} else {// sum大于目标值,将右指针向左移动一位right--;}}// 没有找到满足条件的两个数,返回空数组return new int[]{};}
}

说明:
twoSum 方法接收一个整数数组 nums 和目标值 target,返回一个包含两个数的索引的整数数组,其中这两个数的和等于目标值。
首先使用 Arrays.sort(nums) 对数组进行排序。
初始化左指针 left 指向数组的起始位置,右指针 right 指向数组的结束位置。
使用 while 循环迭代,当左指针小于右指针时:
计算当前左右指针对应元素之和 sum。
如果 sum 等于目标值 target,则返回一个包含左右指针的整数数组。
如果 sum 小于目标值 target,将左指针向右移动一位。
如果 sum 大于目标值 target,将右指针向左移动一位。
如果循环结束后仍然没有找到满足条件的两个数,返回一个空数组。
需要注意的是,在返回结果后,需要及时释放动态分配的内存。

C语言版本
#include <stdio.h>
#include <stdlib.h>int* twoSum(int* nums, int numsSize, int target) {// 对数组进行排序(可以使用快速排序等)qsort(nums, numsSize, sizeof(int), compare);// 初始化左右指针int left = 0;int right = numsSize - 1;while (left < right) {// 当前左右指针对应元素之和int sum = nums[left] + nums[right];if (sum == target) {// 找到目标值,返回对应的数组下标int* result = (int*)malloc(2 * sizeof(int));result[0] = left;result[1] = right;return result;} else if (sum < target) {// sum小于目标值,将左指针向右移动一位left++;} else {// sum大于目标值,将右指针向左移动一位right--;}}// 没有找到满足条件的两个数,返回空数组return NULL;
}int compare(const void* a, const void* b) {// 用于qsort排序的比较函数return (*(int*)a - *(int*)b);
}

说明:
twoSum 函数接受三个参数:指向整数数组 nums 的指针、数组的大小 numsSize 和目标值
target;它返回一个指向整数数组的指针,该数组包含两个数的索引,从给定数组中的两个数之和等于目标值。 twoSum 内部首先调用
qsort 函数对数组进行升序排序,这里使用了自定义的 compare 比较函数。 初始化左右指针 left 和 right
分别指向数组的起始位置和结束位置。 使用 while 循环进行迭代,当左指针小于右指针时,执行以下操作: 计算左右指针对应元素的和 sum。
如果 sum 等于目标值 target,创建一个大小为 2 的动态整型数组 result,将左右指针的值分别赋给 result[0] 和
result[1],然后返回 result。 如果 sum 小于目标值 target,将左指针向右移动一位。 如果 sum 大于目标值
target,将右指针向左移动一位。 如果结束循环后仍未找到满足条件的两个数,返回 NULL 表示未找到。 compare
是用于升序排序的比较函数,用于传递给 qsort 函数。 请注意,在使用完返回的结果后,务必使用 free函数释放动态分配的内存,防止内存泄漏。

Python3版本
class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:# 对数组进行排序nums.sort()left = 0  # 左指针right = len(nums) - 1  # 右指针while left < right:# 当前左右指针对应元素之和sum = nums[left] + nums[right]if sum == target:# 找到目标值,返回对应的数组下标return [left, right]elif sum < target:# sum小于目标值,将左指针向右移动一位left += 1else:# sum大于目标值,将右指针向左移动一位right -= 1# 没有找到满足条件的两个数,返回空数组return []

说明:
首先使用 nums.sort() 对数组进行排序。 初始化左指针 left 指向数组的起始位置,右指针 right指向数组的结束位置。 使用 while 循环迭代,当左指针小于右指针时: 计算当前左右指针对应元素之和。 如果和等于目标值
target,则返回包含左右指针的列表。 如果和小于目标值 target,将左指针向右移动一位。 如果和大于目标值
target,将右指针向左移动一位。 如果循环结束后仍然没有找到满足条件的两个数,返回一个空数组。

复杂度分析

  • 时间复杂度:O(n log n),该解法的时间复杂度取决于排序算法的时间复杂度,其中n表示数组的长度
  • 空间复杂度:O(1),只需要常数级的额外空间。请确保在使用完结果后释放动态分配的内存

总结

解法思路优点缺点时间复杂度空间复杂度
暴力法使用两层循环遍历所有可能的组合,找到符合要求的组合简单直观,易于理解和实现时间复杂度高,需要遍历所有可能的组合O(n^2)O(1)
哈希表遍历数组,使用哈希表记录每个数的索引,查找目标值减去当前数的结果是否存在时间复杂度低,只需遍历一次数组需要额外的空间来存储哈希表O(n)O(n)
双指针法将数组排序后使用左右指针进行查找,根据和与目标值的大小关系不断移动指针时间复杂度低,只需遍历一次数组需要对数组进行排序,改变原始数组的顺序O(n log n)O(1)

相似题目

题目描述难度LeetCode链接
三数之和(3Sum)在给定整数数组中寻找所有不重复的三元组,使得它们的和为0中等LeetCode 15
四数之和(4Sum)在给定整数数组中寻找所有不重复的四元组,使得它们的和为目标值中等LeetCode 18
两数之和 II - 输入有序数组(Two Sum II - Input array is sorted)在有序整数数组中寻找两个数,使得它们的和等于目标值简单LeetCode 167
两数之和 III - 数据结构设计(Two Sum III - Data structure design)设计一个类,支持在一个整数数组中寻找两个数的和等于目标值简单LeetCode 170
两数之和 IV - 输入BST(Two Sum IV - Input is a BST)判断给定二叉搜索树中是否存在两个数的和等于目标值简单LeetCode 653

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++程序员的自我修炼】基础语法篇(二)

风力掀天浪打头 只须一笑不须愁 目录 内联函数 概念&#x1f49e; 性质 ⭐ 不建议变量分离 inline的优劣势 inline的局限性 auto关键字 auto的概念&#x1f49e; auto的使用细则&#x1f49e; auto不能推导的场景 &#x1f49e; auto基于范围的for循环&#x1f49e; 指针空值n…

nginx的安装教程

文章目录 简介nginx安装windows下安装linux下安装 简介 nginx是一个开源的web服务器和反向代理服务器&#xff0c;可以用作负载均衡和HTTP缓存。它处理并发能力是十分强大的&#xff0c;能够经受高负载的考验。 正向代理 Nginx不仅可以做反向代理&#xff0c;实现负载均衡&am…

简单说清楚什么是SQL Injection?

最近看完了《The Pragmatic Programmer: 20th Anniversary Edition, 2nd Edition: Your Journey to Mastery》&#xff0c;在第7章&#xff1a;While You Are Coding的footnotes中&#xff0c;提到了一幅漫画&#xff1a; 这不仅用简单的方式说清楚了什么是SQL Injection&#…

C语言数据结构易错知识点(6)(快速排序、归并排序、计数排序)

快速排序属于交换排序&#xff0c;交换排序还有冒泡排序&#xff0c;这个太简单了&#xff0c;这里就不再讲解。 归并排序和快速排序都是采用分治法实现的排序&#xff0c;理解它们对分支思想的感悟会更深。 计数排序属于非比较排序&#xff0c;在数据集中的情况下可以考虑使…

百度贝塞尔曲线证码识别代码

一、前言 百度出了如图所示的验证码&#xff0c;需要拖动滑块&#xff0c;与如图所示的曲线轨迹进行重合。经过不断研究&#xff0c;终于解决了这个问题。我把识别代码分享给大家。 下面是使用selenium进行验证的&#xff0c;这样可以看到轨迹滑动的过程&#xff0c;如果需要…

关于c# 没有显式头文件的优劣分析

在C#中&#xff0c;没有像C或C中的显式头文件&#xff08;header files&#xff09;的概念。在C#中&#xff0c;源代码文件通常包含类、接口、结构和其他类型的定义&#xff0c;这些源文件直接包含在项目中&#xff0c;并且通过命名空间&#xff08;namespace&#xff09;来组织…

Windows11系统缺少相关DLL解决办法

一.缺少msvcp120.dll 下载Mircrosoft Visual C 2015等系统关键组件 Microsoft Visual C 2015-2022 Redistributable (x86) - 14.34.31931 Installation Error etc.. - Microsoft Q&A 二.缺少python27.dll 重新下载python2.7进行安装(选择Windows x86-64 MSI installer)…

Mybatis分页原理

MyBatis在数据库查询中执行分页操作时&#xff0c;通常会使用分页插件来处理。分页插件能够根据数据库的不同&#xff0c;生成适当的分页查询语句&#xff0c;并将查询结果进行分页处理。下面我将解释MyBatis如何进行分页以及分页插件的一般原理。 MyBatis的分页原理&#xff…

DS2438Z+TR智能电池监测器多场景行业应用解决方案

DS2438ZT&R智能电池监视器为电池组提供了若干很有价值的功能&#xff1a;可用于标识电池组的唯一序列号&#xff1b;直接数字化的温度传感器省掉了电池组内的热敏电阻&#xff1b;可测量电池电压和电流的A/D转换器&#xff1b;集成电流累积器用于记录进入和流出电池的电流总…

前端学习<二>CSS基础——14-CSS3属性详解:Web字体

前言 开发人员可以为自已的网页指定特殊的字体&#xff08;将指定字体提前下载到站点中&#xff09;&#xff0c;无需考虑用户电脑上是否安装了此特殊字体。从此&#xff0c;把特殊字体处理成图片的方式便成为了过去。 支持程度比较好&#xff0c;甚至 IE 低版本的浏览器也能…

文件开头加使用utf-8 的代码是什么

在 Python 文件的开头添加指定使用 UTF-8 编码的代码可以通过如下方式实现&#xff1a; # -*- coding: utf-8 -*-这行代码告诉 Python 解释器该文件使用 UTF-8 编码。将此行添加到 Python 文件的开头可以确保 Python 正确地解析包含非 ASCII 字符的字符串。

【华为OD机试C++】字符个数统计

《最新华为OD机试题目带答案解析》:最新华为OD机试题目带答案解析,语言包括C、C++、Python、Java、JavaScript等。订阅专栏,获取专栏内所有文章阅读权限,持续同步更新! 文章目录 描述输入描述输出描述示例1示例2代码描述 编写一个函数,计算字符串中含有的不同字符的个数。…

格雷希尔G10系列L150A和L200A气动快速连接器,在新能源汽车线束线缆剥线后的气密性测试密封方案

线束线缆在很多用电环境都有使用&#xff0c;比如说新能源汽车&#xff0c;从电池包放电开始&#xff0c;高低压、通讯都开始进行工作&#xff0c;线束在连接的地方需要具有较高的气密性和稳定性&#xff0c;才能保证车辆在不同环境下能够正常的运行。 线束在组装铜鼻子前需要剥…

Linux之 线程池 | 单例模式的线程安全问题 | 其他锁

目录 一、线程池 1、线程池 2、线程池代码 3、线程池的应用场景 二、单例模式的线程安全问题 1、线程池的单例模式 2、线程安全问题 三、其他锁 一、线程池 1、线程池 线程池是一种线程使用模式。线程池里面可以维护一些线程。 为什么要有线程池&#xff1f; 因为在…

C++第十四弹---模板初阶

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、泛型编程 2、函数模板 2.1、函数模板的概念 2.2、函数模板的格式 2.3、函数模板的原理 2.4、函数模板的实例化 2.5、模板参数的匹配原则 …

抽象类和接口(2)(接口部分)

❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; hellohello~&#xff0c;大家好&#x1f495;&#x1f495;&#xff0c;这里是E绵绵呀✋✋ &#xff0c;如果觉得这篇文章还不错的话还请点赞❤️❤️收藏&#x1f49e; &#x1f49e; 关注&#x1f4a5;&a…

增加网站搜索引擎排名的6个准则

怎样提高网站排名首页 在竞争激烈的网络世界中&#xff0c;网站的排名对于吸引流量和提升曝光至关重要。登上搜索引擎结果页面的首页&#xff0c;意味着更多的曝光和点击率。以下是一些方法&#xff0c;可以帮助您提高网站在搜索引擎中的排名&#xff0c;让其跻身首页&#xf…

鸿蒙组件学习_Text组件

说明 Text组件从API Version 7开始支持。 子组件 可以包含Span子组件 代码测试 Text(){Span(hello World).decoration({type: TextDecorationType.Underline,color: Color.Black}).letterSpacing(5).textCase(TextCase.Normal) }span属性: decorationtype: 字体TextDecor…

2014年认证杯SPSSPRO杯数学建模A题(第二阶段)轮胎的花纹全过程文档及程序

2014年认证杯SPSSPRO杯数学建模 A题 轮胎的花纹 原题再现&#xff1a; 轮胎被广泛使用在多种陆地交通工具上。根据性能的需要&#xff0c;轮胎表面常会加工出不同形状的花纹。在设计轮胎时&#xff0c;往往要针对其使用环境&#xff0c;设计出相应的花纹形状。   第二阶段问…

SAP 销售分销中的免费货物

销售业务中&#xff0c;免费货物在您与客户协商价格时起着重要作用。在零售、化工或消费品这样的行业部门中&#xff0c;通常以免费货物的形式向客户提供折扣。 作为用户&#xff0c;业务用户希望能自动确定免费货物并将它们归入销售凭证中。同时需要向成本控制部门提供免费货物…