Two Sum
Given an array of integers, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution.
Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2
Solution:
如果只是简单的:
1 class Solution: 2 # @return a tuple, (index1, index2) 3 def twoSum(self, num, target): 4 for i in range(len(num)): 5 numJ = target-num[i] 6 if numJ in num: 7 return (i+1, num.index(numJ)+1) 8
肯定是会超时的。这个Python程序应该是O(n^3)的(for, 查找numJ, index(numJ)),一般的O(n^2)(用for将查找值和索引合成一个循环)也会超时。
于是很快可以想到另外一种解法:QuickSort + BinarySearch。这是一般的想法,是可以AC的(O(nlogn))。
代码如下:
1 void sort(int a[], int b[], int l, int r) 2 { 3 int i, j, x, t; 4 i = l; j = r; x = a[i + ((j - i) >> 1)]; 5 do 6 { 7 while (x > a[i]) i++; 8 while (x < a[j]) j--; 9 if (i <= j) 10 { 11 t = a[i]; a[i] = a[j]; a[j] = t; 12 t = b[i]; b[i] = b[j]; b[j] = t; 13 i++; j--; 14 } 15 } while (i <= j); 16 if (i < r) sort(a, b, i, r); 17 if (j > l) sort(a, b, l, j); 18 } 19 20 int binSearch(int a[], int e, int lo, int hi) 21 { 22 int mi; 23 while (lo < hi) 24 { 25 mi = (lo + hi) >> 1; 26 if (e < a[mi]) hi = mi; 27 else lo = mi + 1; 28 } 29 return --lo; 30 } 31 32 int *twoSum(int numbers[], int n, int target) { 33 int *index = (int *)malloc(n * sizeof(int)); 34 int i, indexLook; 35 int *ans = (int *)malloc(2 * sizeof(int)); 36 for (i = 0; i<n; i++) index[i] = i; 37 sort(numbers, index, 0, n-1); 38 for (i = 0; i<n; i++) 39 { 40 indexLook = binSearch(numbers, target - numbers[i], 0, n); 41 if (numbers[indexLook] == target - numbers[i]) 42 { 43 if (index[i] < index[indexLook]) 44 { 45 ans[0] = index[i]+1; 46 ans[1] = index[indexLook]+1; 47 } 48 else 49 { 50 ans[1] = index[i]+1; 51 ans[0] = index[indexLook]+1; 52 } 53 return ans; 54 } 55 } 56 }
这里,还有一种也比较常规的解法:HashTable。(O(n))
可以在边建立哈希表的同时也进行查找(按先后顺序,查找第二个时第一个肯定已经入表)。将num中的值映射到其下标,对于每一个num中的值e,在HashTable中查找target-e对应的下标。如果能找到直接返回结果;如果不能找到则把e和其下标也加入哈希表中。
Java解法如下:
1 import java.util.HashMap; 2 import java.util.Map; 3 4 public class Solution { 5 public int[] twoSum(int[] numbers, int target) { 6 Map<Integer, Integer> map=new HashMap<>(numbers.length*2); 7 for(int i=0;i<numbers.length;i++){ 8 Integer company=map.get(target-numbers[i]); 9 if(company==null) 10 map.put(numbers[i], i); 11 else 12 return new int[]{company+1,i+1}; 13 } 14 return null; 15 } 16 }
另外,其实这道题的本质就是找one pair numbers使得其和等于target。我们可以从整体上来看待这个问题,并且根据找到的pair与target之间的关系调整位置继续找,直到找到符合条件的唯一解。
当num排完序后,我们考虑从第一个数(i=0)和最后一个数(j=len-1)开始,如果它们的和比target大,说明此时和需要调整得更小,那么把j指针向前调;如果当前的和比target小,则把i指针向后调。直到找到这样的pair使得和正好等于target。这样就可以避免对于每一个元素e都要对target-e进行二分查找,效率肯定更高。(因为如果当前i一定是最后结果(i, j)中的一个值,那么比当前j下标大的数一定使得当前和比target大,所以j会不断减小直到到正确解,正确性可以得到证明。)
AC代码如下(Python):O(nlogn)
1 class Solution: 2 # @return a tuple, (index1, index2) 3 def twoSum(self, num, target): 4 copyNum = num[:] 5 copyNum.sort() 6 firstIndex = 0 7 lastIndex = len(copyNum) - 1 8 while True: 9 if copyNum[firstIndex] + copyNum[lastIndex] == target: 10 break 11 elif copyNum[firstIndex] + copyNum[lastIndex] > target: 12 lastIndex -= 1 13 else: 14 firstIndex += 1 15 16 fIndex = num.index(copyNum[firstIndex])+1 17 num.reverse() 18 lIndex = len(num) - num.index(copyNum[lastIndex]) 19 20 if fIndex > lIndex: 21 fIndex, lIndex = lIndex, fIndex 22 return (fIndex, lIndex) 23
(因为index只能找到第一个等于该值得下标,当两个加数相等时,就必需从后往前找,所以要num.reverse(),或者自己写一个function从后往前找。)
参考:
1、https://leetcode.com/discuss/27316/java-solution-space-using-binarysearch-time-space-using-hash
2、https://leetcode.com/discuss/26760/python-solution-with-52ms-cost