给定一个整形数组和一个整数n,从数组中找到两个数的索引使这两个数之和等于n,以数组的形式返回这两个数的索引。(假设数组中只存在一组这样的数)
例如:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
循环遍历
首先映入脑海的解决方案,逐步遍历数组中的每个元素,查找是否存在。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class Solution { public int[] twoSum(int[] nums, int target) { int[] result = new int[2]; for(int i=0; i< nums.length; i++) { int first = nums[i]; result[0] = i; int second = target - first; for(int j=i+1; j<nums.length; j++){ if(second == nums[j]){ result[1] = j; break; } } if(result[1] != 0){ break; } } return result; } }
|
代码中涉及两个for循环,注意到第二个for循环其实是在剩余的数组元素中查找是否存在目标数字second,则可以想到如果数组是有序的,则可以使用二分查找来提高效率。
则对上面思路的一种改进是假如数组是无序的,则先使用快排对其进行排序,然后遍历数组中的每一个元素,使用二分查找再数组中查看是否存在second,存在则找到second对应的索引i。
使用hashmap进行改进
从上面的思路中可以看出第一个for循环是不可少的,第二个for循环其实是充当一个查找的功能,则可以使用二分查找进行改进,但是是否可以借助别的数据结构使其能够更快的找到second的索引i。
目的是能够快速的找到second对应的索引i,则second和i是一一对应的关系,存放这种关系的数据结构很容易想到HashMap,也就是将数组中的元素和索引放入map中,元素作为key,这样直接去map中get key对应的value就ok了。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Solution { public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> numsMap = new HashMap<Integer, Integer>(); for(int i=0; i<nums.length; i++){ if(null == numsMap.get(nums[i])){ numsMap.put(nums[i], i); } } int[] result = new int[2]; for(int i=0; i<nums.length; i++){ Integer j = numsMap.get(target - nums[i]); if(null != j){ if(j < i){ result[0] = j; result[1] = i; }else if(j > i){ result[0] = i; result[1] = j; } } } return result; } }
|
对于上述代码依然可以精简,但只是精简代码行数而已,并不能提高时间复杂度。
精简的方法是在第一遍遍历数组的时候就对HashMap进行判断,找到则退出,没有找到则继续循环。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Solution { public int[] twoSum(int[] nums, int target) { Map<Integer, Integer> numsMap = new HashMap<Integer, Integer>(); int[] result = new int[2]; for(int i=0; i<nums.length; i++){ int second = target - nums[i]; if(numsMap.containsKey(second)){ Integer j = numsMap.get(second); if(j < i){ result[0] = j; result[1] = i; }else if(j > i){ result[0] = i; result[1] = j; } break; } if(null == numsMap.get(nums[i])){ numsMap.put(nums[i], i); } } return result; } }
|
总结
遇到此题首先想到的是使用循环遍历找到两个数的索引,其次在遍历的过程中发现第二个for循环其实是遍历查找的功能,说到查找的优化很容易想到的就是二分查找,然后查看是否可以用二分查找,如果不可以则去创造条件使其满足二分查找的条件。
再者在数组中查找一个数是否在数组中,使用hash映射查找只需O(1)的时间复杂度,则构建一个HashMap,使用hash映射查找优化二分查找。
注意,并不是在任何情况下构建HashMap进行查找都会比二分查找要快。因为构建HashMap需要O(n)的时间复杂度去遍历数组。