LeetCode 第一题: 两数之和

文章目录

第一题: 两数之和

题目描述

给定一个整数数组 nums​ 和一个目标值 target​,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组索引。

你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

示例

给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

解题思路

  1. 暴力法:双重循环遍历数组中的每个数,寻找是否存在一个数与它相加等于目标值。
  2. 两遍哈希表法 :遍历数组,将每个数及其索引存入哈希表,然后再次遍历数组,查找 target - nums[i] 是否在哈希表中。
  3. 一遍哈希表法 :遍历数组,对于每个数,查找 target - nums[i] 是否在哈希表中,如果在,直接返回结果;如果不在,将当前数存入哈希表。

截图为Go语言的测试

Go语言实现 - 一遍哈希表法

go 复制代码
package main
func twoSum(nums []int, target int) []int {
    hashTable := make(map[int]int)
    for i, num := range nums {
        complement := target - num
        if index, ok := hashTable[complement]; ok {
            return []int{index, i}
        }
        hashTable[num] = i
    }
    return nil
}

C++实现

\ 复制代码
#include <vector>
#include <unordered_map>

class Solution {
public:
    std::vector<int> twoSum(std::vector<int>& nums, int target) {
        std::unordered_map<int, int> hashTable;
        for (int i = 0; i < nums.size(); ++i) {
            int complement = target - nums[i];
            if (hashTable.find(complement) != hashTable.end()) {
                return {hashTable[complement], i};
            }
            hashTable[nums[i]] = i;
        }
        return {};
    }
};

算法分析

  • 时间复杂度:O(n),其中 n 是数组中的元素数量。我们只遍历了包含有 n 个元素的列表两次。在哈希表中的操作时间复杂度为 O(1)。
  • 空间复杂度 :O(n),所需的额外空间取决于哈希表中存储的元素数量,该表中存储了 n 个元素。
    这个Go语言实现的代码简单易懂,效率较高,适合解决这类问题。

排序和双指针法

  1. 首先,创建一个数组,包含原始数组的元素和它们的索引。

  2. 然后,根据数组元素对数组进行排序。

  3. 使用两个指针,一个从开始(最小元素)的位置,另一个从结束(最大元素)的位置。

  4. 比较两个指针指向的元素之和与目标和:

    • 如果和等于目标和,返回两个元素的索引。
    • 如果和小于目标和,移动左侧指针,使它指向一个更大的数。
    • 如果和大于目标和,移动右侧指针,使它指向一个更小的数。
      这种方法的关键在于,排序后我们可以使用双指针高效地找到两个数,使得它们的和等于目标和。

Go语言实现 - 排序和双指针法

go 复制代码
package main
import (
	"sort"
)
type Pair struct {
	Value int
	Index int
}
func twoSum(nums []int, target int) []int {
	pairs := make([]Pair, len(nums))
	for i, num := range nums {
		pairs[i] = Pair{Value: num, Index: i}
	}
	sort.Slice(pairs, func(i, j int) bool {
		return pairs[i].Value < pairs[j].Value
	})
	left, right := 0, len(nums)-1
	for left < right {
		sum := pairs[left].Value + pairs[right].Value
		if sum == target {
			return []int{pairs[left].Index, pairs[right].Index}
		} else if sum < target {
			left++
		} else {
			right--
		}
	}
	return nil
}

C++

\ 复制代码
#include <vector>
#include <algorithm>
#include <utility>

class Solution {
public:
    std::vector<int> twoSum(std::vector<int>& nums, int target) {
        std::vector<std::pair<int, int>> numWithIndex;
        for (int i = 0; i < nums.size(); ++i) {
            numWithIndex.push_back({nums[i], i});
        }
        std::sort(numWithIndex.begin(), numWithIndex.end());

        int left = 0, right = nums.size() - 1;
        while (left < right) {
            int sum = numWithIndex[left].first + numWithIndex[right].first;
            if (sum == target) {
                return {numWithIndex[left].second, numWithIndex[right].second};
            } else if (sum < target) {
                ++left;
            } else {
                --right;
            }
        }
        return {};
    }
};

算法分析

  • 时间复杂度:O(nlogn),其中 n 是数组中的元素数量。排序的时间复杂度是 O(nlogn),双指针遍历的时间复杂度是 O(n)。
  • 空间复杂度 :O(n),我们需要一个额外的数组来存储元素和它们的索引。
    这种方法在空间复杂度上与哈希表方法相同,但时间复杂度稍高,因为它需要排序。然而,这种方法在不允许修改原数组或需要保持元素原始顺序的情况下可能更有用。

暴力法

  1. 使用两个嵌套循环遍历数组中的所有元素对。
  2. 对于每一对元素,检查它们的和是否等于目标和。
  3. 如果找到一对元素的和等于目标和,返回这两个元素的索引。
    这种方法的时间复杂度是 O(n^2),因为它需要对数组中的每一对元素进行一次检查。

Go语言实现 - 暴力法

go 复制代码
package main
func twoSum(nums []int, target int) []int {
    for i := 0; i < len(nums); i++ {
        for j := i + 1; j < len(nums); j++ {
            if nums[i] + nums[j] == target {
                return []int{i, j}
            }
        }
    }
    return nil
}

C++

\ 复制代码
#include <vector>

class Solution {
public:
    std::vector<int> twoSum(std::vector<int>& nums, int target) {
        for (int i = 0; i < nums.size(); ++i) {
            for (int j = i + 1; j < nums.size(); ++j) {
                if (nums[i] + nums[j] == target) {
                    return {i, j};
                }
            }
        }
        return {};
    }
};

算法分析

  • 时间复杂度:O(n^2),其中 n 是数组中的元素数量。最坏的情况下,我们需要遍历每个元素与其他元素的组合。
  • 空间复杂度 :O(1),我们只需要常数级别的额外空间来存储索引。
    暴力法是一种简单直接的方法,但是对于大型数据集来说,它的效率不高。在实际应用中,通常会优先考虑使用哈希表或排序和双指针法来解决这类问题。

二分搜索法

  1. 首先,对数组进行排序,并创建一个新数组来保存排序后元素的原始索引。
  2. 对于数组中的每个元素,使用二分搜索查找 target - nums[i]
  3. 如果找到,确保两个元素的原始索引不同,然后返回它们的原始索引。
    这种方法的关键在于,排序后我们可以使用二分搜索高效地找到第二个数。

Go语言实现 - 二分搜索法

go 复制代码
package main
import (
	"sort"
)
func twoSum(nums []int, target int) []int {
	indexes := make([]int, len(nums))
	for i := range nums {
		indexes[i] = i
	}
	sort.Slice(indexes, func(i, j int) bool {
		return nums[indexes[i]] < nums[indexes[j]]
	})
	for i := 0; i < len(nums); i++ {
		left, right := i+1, len(nums)-1
		complement := target - nums[indexes[i]]
		for left <= right {
			mid := left + (right-left)/2
			if nums[indexes[mid]] == complement {
				return []int{indexes[i], indexes[mid]}
			} else if nums[indexes[mid]] < complement {
				left = mid + 1
			} else {
				right = mid - 1
			}
		}
	}
	return nil
}

C++

\ 复制代码
#include <vector>
#include <algorithm>
#include <numeric>

class Solution {
public:
    std::vector<int> twoSum(std::vector<int>& nums, int target) {
        std::vector<int> indexes(nums.size());
        std::iota(indexes.begin(), indexes.end(), 0);
        std::sort(indexes.begin(), indexes.end(), [&nums](int i, int j) { return nums[i] < nums[j]; });

        for (int i = 0; i < nums.size(); ++i) {
            int left = i + 1, right = nums.size() - 1;
            int complement = target - nums[indexes[i]];
            while (left <= right) {
                int mid = left + (right - left) / 2;
                if (nums[indexes[mid]] == complement) {
                    return {indexes[i], indexes[mid]};
                } else if (nums[indexes[mid]] < complement) {
                    left = mid + 1;
                } else {
                    right = mid - 1;
                }
            }
        }
        return {};
    }
};

算法分析

  • 时间复杂度:O(nlogn),其中 n 是数组中的元素数量。排序的时间复杂度是 O(nlogn),二分搜索的时间复杂度是 O(logn),总共进行了 n 次二分搜索。
  • 空间复杂度 :O(n),我们需要一个额外的数组来保存索引。
    二分搜索法在空间复杂度上与哈希表方法相同,但时间复杂度稍高,因为它需要排序。这种方法在不允许修改原数组或需要保持元素原始顺序的情况下可能更有用。然而,对于"两数之和"这个问题,哈希表法通常是更优的选择,因为它的时间复杂度更低。

C++的实现

相关推荐
爱吃生蚝的于勒1 小时前
C语言内存函数
c语言·开发语言·数据结构·c++·学习·算法
吾店云建站4 小时前
WordPress 6.7 “Rollins”发布
科技·程序人生·职场和发展·创业创新·程序员创富
ChoSeitaku6 小时前
链表循环及差集相关算法题|判断循环双链表是否对称|两循环单链表合并成循环链表|使双向循环链表有序|单循环链表改双向循环链表|两链表的差集(C)
c语言·算法·链表
DdddJMs__1356 小时前
C语言 | Leetcode C语言题解之第557题反转字符串中的单词III
c语言·leetcode·题解
Fuxiao___6 小时前
不使用递归的决策树生成算法
算法
我爱工作&工作love我6 小时前
1435:【例题3】曲线 一本通 代替三分
c++·算法
白-胖-子7 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
workflower7 小时前
数据结构练习题和答案
数据结构·算法·链表·线性回归
好睡凯7 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
Sunyanhui17 小时前
力扣 二叉树的直径-543
算法·leetcode·职场和发展