1. Two Sum

来源:互联网 发布:人工智能医疗大会 编辑:程序博客网 时间:2024/06/01 13:28

题目链接1. Two Sum

思考过程:

最容易想到的思路是直接遍历每个元素,然后用target 减去这个元素得到一个数N,接着再遍历整个数组去寻找看N在不在数组中。这样做的时间复杂度为O(n^2)

n^2的复杂度来至于2个方面,第一,是遍历每个元素复杂度O(n),第二是对于每个元素与target相减得到的数N都需要复杂度为O(n)的查找操作,两者是相乘关系,所以优化带来的效益是一样的。
很显然,第一点是无法优化的,因为我们至少需要遍历一遍所有元素,所以关注点应该是第二点,即在获得某个数的前提下去数组中查找该数是否存在,对于这题而言,如果该数存在,还需要知道其下标。

问题转变成,如何快速在数组中查找某个元素的下标,并且时间复杂度要低于O(n), 我们立刻想到二分查找,但是二分查找的前提是数组有序,题目未指明该前提,故不可用。
另外一个思路是hash,对于整数而言,如果存在数据范围,可直接用其作为下标进行hash,但是题目未明确说明数据范围,故不可用,即O(1) hash不可用。
进而求其次考虑O(logn) hash,立刻想到用map,C++中map的查找复杂度即O(logn)。
这样总复杂度降低为O(nlogn)

可以首先遍历数组,建立其map<数,下标 + 1> ,下标 + 1是为了用0表示数不存在
然后遍历数组,查找target - num在map中是否存在, 如果存在则,则答案找到。

细节思考:

  1. 题目并没有明确说明不存在重复数,所以要考虑重复数,假设存在重复数,那么有两种情况,第一种就是重复数是答案,那么我们可以断定,只有当重复数是target/2的时候才有可能,因为如果是重复数与另外一个数组成了答案,那么必然存在2个以上的答案组,这个情况题目确定说了不可能,所以我们当我们发现重复数的时候,只需要判断是否是target/2, 如果不是,则该数的所有副本都不可能是答案,所以关于重复数的其他所有问题都无需考虑。
  2. 思路中的两步可以合成一步,即一边建立map,一边遍历数组。

参考代码:

class Solution {public:    vector<int> twoSum(vector<int>& nums, int target) {        map<int, int> nums_map;        vector<int> ans;        for (int i = 0; i < nums.size(); i++){            //只有这种情况会出现两个相同的数且答案中含有这个数            if (nums_map[nums[i]] != 0 && target - nums[i] == nums[i]){                ans.push_back(nums_map[nums[i]] - 1);                ans.push_back(i);                break;            }            //试着寻找答案            nums_map[nums[i]] = i + 1;            if (nums_map[target - nums[i]] != 0 && nums_map[target - nums[i]] != i + 1){                ans.push_back(nums_map[target - nums[i]] - 1);                ans.push_back(i);                break;            }        }        return ans;    }};
2 0