一、问题描述:两数之和
给定一个整数数组 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 <=
-109 <= nums[i] <=
-109 <= target <=
- 只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?
二、实现方案
方案一:暴力求解:两次for 循环;
- 耗时:720ms; 时间复杂度O(n2)。
- 内存消耗:20.89MB ; 空间复杂度O(1)。
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function twoSum($nums, $target) {
$result = [];
$count = count($nums);
for($i=0;$i<$count-1;$i++){
for($j=$i+1;$j<$count;$j++){
if($nums[$i]+$nums[$j] == $target){
$result=[$i,$j];
break;
}
}
}
return $result;
}
}
方案二:哈希表:一次for 循环 + 函数 array_keys()
- 耗时:51ms ;时间复杂度O(n)。
- 内存消耗:21.17MB; 空间复杂度 O(n)。
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function twoSum($nums, $target) {
$find = [];
$count = count($nums);
for ($i = 0; $i < $count; $i++) {
$first = $nums[$i];
if ($second = array_keys($find, ($target - $first))) {
return [$second[0], $i];
}
$find[$i] = $first;
}
}
}
方案三:哈希表:一次for 循环 + 函数 array_key_exists()
- 耗时:0ms; 时间复杂度O(n)。
- 内存消耗21.05MB:空间复杂度 O(n)。
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function twoSum($nums, $target) {
$found = [];
$count = count($nums);
for ($i = 0; $i < $count; $i++) {
$diff = $target - $nums[$i];
if (array_key_exists($diff, $found)) {
return [$found[$diff], $i];
}
$found[$nums[$i]] = $i;
}
}
}
方案四:哈希表:array_flip() 函数实现数组反转+foreach()循环。
- 耗时 1ms ,时间复杂度O(n)。
- 内存分布:21.31MB ;空间复杂度 O(n)。
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function twoSum($nums, $target) {
$flip_arr = array_flip($nums);
foreach($nums as $k=>$v){
if($flip_arr[$target-$v] && $k!=$flip_arr[$target-$v]){
return [$k,$flip_arr[$target-$v]];
}
}
}
}
方案五:哈希表:foreach()循环+isset()
- 耗时 0ms ,时间复杂度O(n)。
- 内存分布:20.96MB ;空间复杂度 O(n)。
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer[]
*/
function twoSum($nums, $target) {
$found = [];
foreach ($nums as $key => $val) {
$diff = $target - $val;
if (!isset($found[$diff])) {
$found[$val] = $key;
continue;
}
return [$found[$diff], $key];
}
}
}
方案六:两次遍历哈希表
- 耗时 0ms ,时间复杂度O(n)。
- 内存分布:20.79MB ;空间复杂度 O(n)。
function twoSumTwoPassHash($nums, $target) {
$numMap = [];
// 第一次遍历存哈希表
foreach ($nums as $i => $val) {
$numMap[$val] = $i; // 重复值会覆盖,保留最后出现的索引
}
// 第二次遍历找补数
foreach ($nums as $i => $num) {
$complement = $target - $num;
if (isset($numMap[$complement]) && $numMap[$complement] != $i) {
return [$i, $numMap[$complement]];
}
}
return [];
}
方案七:双指针法(需排序)
- 耗时:16ms;时间复杂度:O(Nlogn)。
- 内存分布:25 MB ;空间复杂度 O(n)。
function twoSumTwoPointers($nums, $target) {
// 绑定值和索引
$elements = [];
foreach ($nums as $index => $value) {
$elements[] = ['value' => $value, 'index' => $index];
}
// 按值排序
usort($elements, function($a, $b) {
return $a['value'] - $b['value'];
});
// 双指针查找
$left = 0;
$right = count($elements) - 1;
while ($left < $right) {
$sum = $elements[$left]['value'] + $elements[$right]['value'];
if ($sum == $target) {
return [$elements[$left]['index'], $elements[$right]['index']];
} elseif ($sum < $target) {
$left++;
} else {
$right--;
}
}
return [];
}
三、总结
- 暴力法:直接但效率低(O(n²)),适用于小数据量。
- 双指针法:需排序(O(n log n)),适合允许修改输入的场景。
- 两次遍历哈希表:哈希表存储后查找(O(n)),但需要两次遍历。
- 一次遍历哈希表:最优解(O(n)),边遍历边检查补数是否存在。