[LeetCode] NO.1 Two Sum
题目:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution.
Example:
Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
解析过程:
拿到题目,直接能想到的是最简单也是最笨的方法就是对数组进行遍历,代码如下:
public static int[] twoSum(int[] nums, int target) { int indices[] = new int[2]; for(int i = 0; i < nums.length - 1; i++){ for(int j = i+1; j < nums.length; j++){ if((i < j) && (nums[i] + nums[j] == target)){ indices[0] = i; indices[1] = j; } } } return indices; }
显然这样的策略是耗时的,时间复杂度为O(n2)。
那么,有没有比较高效的方法呢?根据动态规划的思想,我们可以将问题进行划分。设数组长度为len,我们需要求满足条件的i,j使得 nums[i]+nums[j] = target,我们不妨来看数组最后一项nums[len-1],根据该项是否包含到最终返回结果,可以分两种情况。当len-1 = j时,我们只需要遍历数组的0~len-2项,取nums[i] = target - nums[len-1]的i即可。当len-1 != j时,我们知道我们所求的i和j,也就转化成原数组的子数组(从0到len-2项)上的问题。这样便可以把问题不断进行划分,临界条件是当数组长度等于2的时候。代码如下:
public static int[] twoSum(int[] nums, int target) { return twoSum(nums,nums.length,target); } public static int[] twoSum(int[] nums, int n, int target) { int indices[] = new int[2]; if(n >= 2){ int j = n-1; int part = target - nums[j]; for(int i = 0; i < j; i++) { if(nums[i] == part){ indices[0] = i; indices[1] = j; return indices; } } return twoSum(nums,n-1,target); } return null; }
运行代码,效率提高不少,但是依然不是很高效。时间复杂度还是O(n2),代码里用到了递归的实现方式,我们都知道递归是耗时的,凡是用递归可以实现的,都可以用循环和栈来实现,这个不做探讨。
我们可以设想,如果数组是有序的,我们解决这个问题是不是比较容易了?有序数组的话,我们可以从数组两端取值,比较两个数的和与目标数target的大小,来决定移动左端还是右端,这样循环下来,一定能找到我们的答案,这样遍历一下的时间复杂度只有O(n)。那么,我们可以考虑给数组排序,排序的时间复杂度在O(nlgn),但是排序之后数组顺序乱了?我们还要找原来的index,怎么办? 这个好办,我们可以再开辟一个数组,用来存最原始的数组,然后找到目标值之后,然后再到这个原数组copy数组里面去定位对应的index即可。代码如下:
public static int[] twoSum(int[] nums, int target) { int[] copynums = Arrays.copyOf(nums, nums.length); boolean aflag = false,bflag = false; Arrays.sort(nums); int indices[] = new int[2]; int start = 0; int end = nums.length - 1; int a = 0 ,b = 0; while(start < end){ int sum = nums[start] + nums[end]; if(sum < target){ start++; }else if(sum > target){ end--; }else if(sum == target){ a = nums[start]; b = nums[end]; break; } } for(int i = 0; i < copynums.length; i++){ if(!aflag && (copynums[i] == a)){ indices[0] = i; aflag = true; continue; } if(!bflag && (copynums[i] == b)){ indices[1] = i; bflag = true; } if(aflag && bflag && (indices[0] != indices[1])){ break; } } Arrays.sort(indices); return indices; }
排序的时候我们调用了Arrays.sort方法,具体实现可以查看源代码。
运行上面的代码,我们发现,效率有了质的提高,可以达到我们的要求了。