大家好,我是漫步coding, 最近在整理2022年Redis最新面試題, 大家也可以通過我下面的博客地址在線閱讀,今天講講LeetCode高頻算法面試題 - 兩數(shù)之和。本文首發(fā)于公眾號:漫步coding
題目來源于 LeetCode 上第 1 號問題:兩數(shù)之和。題目難度為 Easy。
題目描述
給定一個整數(shù)數(shù)組 nums
和一個目標值 target
,請你在該數(shù)組中找出和為目標值的那 兩個 整數(shù),并返回他們的數(shù)組下標杠人。
你可以假設(shè)每種輸入只會對應(yīng)一個答案。但是宋下,你不能重復利用這個數(shù)組中同樣的元素嗡善。
題目難度: ★
示例:
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
示例 2:
輸入:nums = [3,2,4], target = 6
輸出:[1,2]
示例 3:
輸入:nums = [3,3], target = 6
輸出:[0,1]
題目解析
使用查找表來解決該問題。
設(shè)置一個 map 容器 record 用來記錄元素的值與索引杨凑,然后遍歷數(shù)組 nums滤奈。
- 每次遍歷時使用臨時變量 complement 用來保存目標值與當前值的差值
- 在此次遍歷中查找 record ,查看是否有與 complement 一致的值撩满,如果查找成功則返回查找值的索引值與當前變量的值 i
- 如果未找到蜒程,則在 record 保存該元素與索引值 i
代碼實現(xiàn)
tips: 以下代碼是使用Go代碼實現(xiàn)的不同解法, 文章最后可以看C++、C伺帘、Java昭躺、Python實現(xiàn)
1、暴力解法
最容易想到的方法是枚舉數(shù)組中的每一個數(shù) x伪嫁,尋找數(shù)組中是否存在 target - x领炫。
當我們使用遍歷整個數(shù)組的方式尋找 target - x 時,需要注意到每一個位于 x 之前的元素都已經(jīng)和 x 匹配過张咳,因此不需要再進行匹配帝洪。而每一個元素不能被使用兩次,所以我們只需要在 x 后面的元素中尋找 target - x脚猾。
func twoSum(nums []int, target int) []int {
numsLen := len(nums)
for i, num := range nums {
for j := i + 1; j < numsLen; j++ {
if target == num + nums[j] {
return []int{i, j}
}
}
}
return []int{}
}
執(zhí)行結(jié)果:
- 時間復雜度:O(N^2)葱峡,其中 N 是數(shù)組中的元素數(shù)量。最壞情況下數(shù)組中任意兩個數(shù)都要被匹配一次龙助。
- 空間復雜度:O(1)砰奕。
內(nèi)存方面還可以,不過運行時間偏慢, 時間復雜度太高了。
2军援、使用map方式, nums 的值進行倒排索引, 以空間換時間仅淑, 時間復雜度是O(n)
func twoSum(nums []int, target int) []int {
numsMap := make(map[int]int)
for index, num := range nums{
numsMap[num] = index
}
for index, num := range nums {
if preIndex, ok := numsMap[target-num]; (ok && preIndex != index) {
return []int{preIndex, index}
}
}
return []int{}
}
執(zhí)行結(jié)果:
時間復雜度:O(n),我們只遍歷了包含有O(n)個元素的列表兩次胸哥。在表中進行的每次查找只花費 O(1)的時間涯竟。
空間復雜度:O(n),所需的額外空間取決于哈希表中存儲的元素數(shù)量烘嘱,該表最多需要存儲 N個 元素昆禽。
3、使用map方式, nums 的值進行倒排索引蝇庭, 一遍哈希表
事實證明,我們可以一次完成捡硅。在進行迭代并將元素插入到表中的同時哮内,我們還會回過頭來檢查表中是否已經(jīng)存在當前元素所對應(yīng)的目標元素。如果它存在壮韭,那我們已經(jīng)找到了對應(yīng)解北发,并立即將其返回。
func twoSum(nums []int, target int) []int {
numsMap := make(map[int]int)
for index, num := range nums {
if preIndex, ok := numsMap[target-num]; ok {
return []int{preIndex, index}
}
numsMap[num] = index
}
return []int{}
}
執(zhí)行結(jié)果:
時間復雜度:O(n)喷屋,我們只遍歷了包含有O(n)個元素的列表一次琳拨。在表中進行的每次查找只花費 O(1)的時間。
空間復雜度:O(n)屯曹,所需的額外空間取決于哈希表中存儲的元素數(shù)量狱庇,該表最多需要存儲 N個元素。
4恶耽、雙指針游標
雙指針的思路
- 將原數(shù)組排好序, 設(shè)置兩個指針left, right
- left 從0開始, right 做 len(nums) - 1 開始
- 當nums[left] + nums[right] < target時密任,我們就沒有必要對所有的right0 ∈(left, right),因為nums[left] + nums[right0] 一定是比target小的, 所以left = left + 1后重新判斷
- 當nums[left] + nums[right] > target時偷俭,同樣的浪讳,對left0 ∈(left, right),必然有nums[left0] + nums[right] > target, 所以right = right - 1后重新判斷
- 當nums[left] + nums[right] = target時涌萤,就是我們想要知道的兩個數(shù)值了
- 然后利用Map將數(shù)值轉(zhuǎn)換為之前的游標
func twoSum(nums []int, target int) []int {
type Node struct{
Value int
Next *Node
}
// 用鏈表是解決數(shù)組重復問題
numsMap := make(map[int]*Node)
var current *Node
for index, num := range nums{
if _, ok := numsMap[num]; ok{
current = numsMap[num]
for {
if current.Next == nil{
current.Next = &Node{index, nil}
break
}
current = current.Next
}
}else{
numsMap[num] = &Node{index, nil}
}
}
sort.Ints(nums)
left := 0
right := len(nums) - 1
for {
if left >= right{
break
}
sum := nums[left] + nums[right]
if sum == target{
if nums[left] == nums[right]{
return []int{numsMap[nums[left]].Value, numsMap[nums[left]].Next.Value}
}else{
return []int{numsMap[nums[left]].Value, numsMap[nums[right]].Value}
}
}
if sum < target{
left ++
}else{
right --
}
}
return []int{}
}
執(zhí)行結(jié)果:
因為要排序淹遵,時間復雜度也比較高, 需要Map存儲游標,空間復雜度也比較高, 算是一個不太好的實現(xiàn)方式负溪。
其他語言版本
C++
// 1. Two Sum
// 時間復雜度:O(n)
// 空間復雜度:O(n)
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> record;
for(int i = 0 ; i < nums.size() ; i ++){
int complement = target - nums[i];
if(record.find(complement) != record.end()){
int res[] = {i, record[complement]};
return vector<int>(res, res + 2);
}
record[nums[i]] = i;
}
return {};
}
};
執(zhí)行結(jié)果
[圖片上傳失敗...(image-51924b-1653220059347)]
C
// 1. Two Sum
// 時間復雜度:O(n)
// 空間復雜度:O(n)
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
int *ans=(int *)malloc(2 * sizeof(int));
int i,j;
bool flag=false;
for(i=0;i<numsSize-1;i++)
{
for(j=i+1;j<numsSize;j++)
{
if(nums[i]+nums[j] == target)
{
ans[0]=i;
ans[1]=j;
flag=true;
}
}
}
if(flag){
*returnSize = 2;
}
else{
*returnSize = 0;
}
return ans;
}
執(zhí)行結(jié)果
Java
// 1. Two Sum
// https://leetcode.com/problems/two-sum/description/
// 時間復雜度:O(n)
// 空間復雜度:O(n)
class Solution {
public int[] twoSum(int[] nums, int target) {
int l = nums.length;
int[] ans=new int[2];
int i,j;
for(i=0;i<l-1;i++)
{
for(j=i+1;j<l;j++)
{
if(nums[i]+nums[j] == target)
{
ans[0]=i;
ans[1]=j;
}
}
}
return ans;
}
}
執(zhí)行結(jié)果
Python
# 1. Two Sum
# https://leetcode.com/problems/two-sum/description/
# 時間復雜度:O(n)
# 空間復雜度:O(n)
class Solution(object):
def twoSum(self, nums, target):
l = len(nums)
print(nums)
ans=[]
for i in range(l-1):
for j in range(i+1,l):
if nums[i]+nums[j] == target:
ans.append(i)
ans.append(j)
print([i,j])
break
return ans
執(zhí)行結(jié)果
結(jié)語
綜合來看透揣, 選擇方式3比較好: 使用map方式, nums 的值進行倒排索引, 一遍哈希表, Go的實現(xiàn)在內(nèi)存方面和時間復雜度都還可以笙以, Python相對表現(xiàn)就差很多淌实。
幾種語言運行效果對比
Go程序在內(nèi)存方面表現(xiàn)最佳, Python在內(nèi)存和運行時間表現(xiàn) 比較其他的語言都比較差。