Leetcode1——两数之和 详细解析
Leetcode1——两数之和
题目分类:数组、哈希表的应用
1. 题目描述
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。(原题网址:https://leetcode-cn.com/problems/two-sum/)
示例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. 解法
2.1 暴力匹配
这个应该是最容易想到的解法了,既然答案说了一定只有一个组合成立,我们只要使用两个for循环,遍历一下nums数组,如果nums[i] + nums[j] == target,那么就输出对应的i和j即可。
class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i+1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return new int[] {i, j};
}
}
}
return new int[0];
}
}
注意:有返回值的Java方法要求最后一定有确定的返回值,即你的return语句不能全部都在条件分支里面(万一分支条件均不满足,那么就出错了),所以在最后一行也会放上一个return语句,像本题我们放上一个长度为0的数组(代表没有找到结果),但是题中描述说必有一个唯一解,实际上这个语句永远不会运行,若是不加则会报错哦。
复杂度分析
假设问题的规模为n(这里即是数组长度为n)
我们使用了两个for循环,最坏的情况是比较全部元素后才发现目标元素,即比较次数为\(\frac{n(n-2)}{2}\),那么时间复杂度为\(O(n^2)\)。额外存储空间为常数,故空间复杂度为\(O(1)\)。
能不能将时间复杂度降低一些呢?首先来分析一下暴力破解法,其相当于选定nums[i]再从i到n-1中选取target - nums[i],而这个寻找target - nums[i]的操作时间复杂度是\(O(n)\),这是因为我们无法直接确定target - nums[i]的位置,需要逐个比较确定。
2.2 哈希表解法
暴力破解法的困难可以用HashMap来解决,我们可以将nums[i]——i以键值对的形式存储在哈希表中,这样就可以以O(1)的时间复杂度找到target - nums[i]。
2.2.1 一个笨笨的代码
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hash = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
hash.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
while (hash.containsKey(target - nums[i]) &&
(hash.get(target - nums[i]) != i)) {
return new int[] {hash.get(target - nums[i]), i};
}
}
return new int[0];
}
}
代码分析
首先将全部键值对存到哈希表中,这个操作时间复杂度为O(n)。接下来使用一个for循环来遍历nums,注意,这里内层仍选用了一个while循环!这个while循环是为了避免在哈希表查找到i(也就是当前遍历到的)的值,这样就相当于使用了一个元素多次。所以,当查找到的数组下标为仍为i时,需要重新查找一下,找到除了当前元素的其它target - nums[i]。这个while最多只会运行2次,所以不会是时间复杂度为\(O(n^2)\),该步骤时间复杂度仍为O(n)。故整体的时间复杂度仍为O(n)。时间复杂度大大降低啦!但是代价是空间复杂度飙升到O(n)。
2.2.2 更聪明的代码
在使用哈希表时,我们先将nums[i]和i全部存到哈希表中再进行查找,能不能把代码进一步优化一下呢?而且上一组代码的while使用有些丑陋,下面给出更优雅的实现(leetcode官方题解)
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashtable = new HashMap<Integer, Integer>();
for (int i = 0; i < nums.length; ++i) {
if (hashtable.containsKey(target - nums[i])) {
return new int[]{hashtable.get(target - nums[i]), i};
}
hashtable.put(nums[i], i);
}
return new int[0];
}
}
官方的解法通过在寻找target - nums[i]后构造哈希表的方式,有效避免了哈希表查到自己的麻烦(笨笨的解法),而且缩短了代码行数。
如何避免麻烦的解释:在查找target - nums[i]时,nums[i]的键值对未存到哈希表中,也就是此时只能查到数组下标不是i但值为target - nums[i]的元素。而且只用了一个for循环就解决了问题,十分简洁。
3. 总结
本题虽然很简单,但还是有着许多解法的,本题除了以上两种解法还可以先将数组排序(不妨设选用的是平均时间复杂度为O(nlogn)的排序,例如快速排序、归并排序),在遍历有序数组的时候利用二分查找快速查找target - nums[i],整体平均时间复杂度为O(nlogn),这同样也是一种很好的思路。在学习算法的时候也是一样,在解出题目后要分析其时间和空间复杂度,看看能否优化。
Leetcode1——两数之和 详细解析的更多相关文章
- LeetCode1——两数之和
最近在家拧三阶魔方,把初级的玩法掌握了,也就是可以还原六个面了,速度不快,但是也很兴奋.三阶魔方的初级玩法按照套路拧就可以了,每一步需要完成的任务,该步骤转动的方法基本都是固定的,而且变化也并不是特别 ...
- Leetcode-1.两数之和
题目描述: 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,你不能重复利用这个数 ...
- [Swift]LeetCode1 .两数之和 | Two Sum
Given an array of integers, return indices of the two numbers such that they add up to a specific ta ...
- LeetCode1.两数之和 JavaScript
给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 实例: 给定 nums = [2, 7, 11, 15], target ...
- Leetcode1.两数之和——简洁易懂
> 简洁易懂讲清原理,讲不清你来打我~ 输入一个数组和一个整数,从数组中找到两个元素和为这个整数,输出下标 题目描述 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数: 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 例如: 给定 nums = [2,7,11, ...
- 给定数组A,大小为n,现给定数X,判断A中是否存在两数之和等于X
题目:给定数组A,大小为n,现给定数X,判断A中是否存在两数之和等于X 思路一: 1,先采用归并排序对这个数组排序, 2,然后寻找相邻<k,i>的两数之和sum,找到恰好sum>x的 ...
随机推荐
- 【C#基础概念】字节顺序(大端、小端)
字节顺序,又称端序或尾序(英語:Endianness),在计算机科学领域中,指電腦記憶體中或在数字通信链路中,组成多字节的字的字节的排列顺序. 例如假设上述变量x类型为int,位于地址0x100处,它 ...
- 【windows 访问控制】十二、C#实操 主体 System.Security.Principal 案例
案例1.主体(包含用户和组)和标识(用户名)的使用. PrincipalPolicy枚举:主体类型 分为window主体.未认证的主体和未分配主体GenericPrincipal.GenericIde ...
- 【C#表达式树 七】 反射在表达式树中的应用 ListInitExpression
以下都是反射在表达式树中的应用 对象初始化 Expression.MemberInit 反射获取成员(字段 或者属性),绑定数据,然后生成 成员表达式节点 class Animal { public ...
- Qt:QJsonValue
0.说明 QJsonValue类用于操作JSON中的各种数据. JSON是用于存储结构化数据的格式,JSON中的数据可以是六种类型: 基本类型 存储类型 bool QJsonValue::Bool d ...
- 在 Debian 和 Ubuntu 上安装 Cutefish 可爱鱼
版权声明:原创文章,未经博主允许不得转载 CutefishOS 是一个可爱好看的新 Linux 发行版,当前最新版本为 0.8beta .这是一个基于 Debian 的发行版,从其镜像源配置就可以明显 ...
- laravel 框架登录 实际操作
//登录中间件 Route::group(['middleware'=>'checkage'],function (){ Route::get('/mou/list','MouControlle ...
- php 23种设计模型 - 策略模式
介绍 意图:定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换,用户还不需要知道其具体的实现 主要解决:在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护. 何时使 ...
- Map 集合
<零基础学Java > Map 集合 在程序中需要存储具有映射关系就需要使用Map集合了. Map集合由Map接口.Map接口的实现类组成. Map 接口 Map接口没有Collectio ...
- KDT入门小讲
KDT入门小讲 为了搞讲课两天搞出来的PPT,质量不高,随便看看 附:讲课用PPT 链接: https://pan.baidu.com/s/1qHea0fEhscAsQh8-Yu_j_A 提取码: 4 ...
- EXSI6.7 中给虚拟机磁盘扩容
[admin@localhost ~]$ sudo fdisk -l Disk /dev/sda: 214.7 GB, 214748364800 bytes, 419430400 sectors Un ...