[Chinese ver]1.兩數求和 。給定一個整數的陣列,返回兩個數字的索引使得這兩個數字加起來成為一個指定的目標值。
你可以假設每個輸入都至少有一個解決方案,並且你不能使用相同的元素兩次。
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].複製程式碼
首先是一次錯誤的嘗試
public class Solution {
public int[] twoSum(int[] nums, int target) {
int[] testNums = new int[nums.length];
int j = 0;
for (int i=0;i<nums.length;i++){
if(nums[i]<target){
testNums[j] = nums[i];
j=j+1;
}
}
for(int l=0;l<j;l++){
for(int k=l+1;k<j;k++){
if(testNums[l]+testNums[k]==target){
return new int[]{l,k};
}
}
}
return null;
}
}複製程式碼
沒理解好題意,這種解法最後得到的是我們自定義陣列的序號,而不是題目要求的序號,暈倒。。。後來發現一個更嚴重的問題。。他沒有說過不能有負數!!!!也就不需要判斷陣列裡的值是否大於和。。。
然後重新構思一下,先來個基本的解法
方法一:暴力迴圈
public class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i=0;i<nums.length;i++){
for(int k=i+1;k<nums.length;k++){
if(nums[i]+nums[k]==target){
return new int[]{i,k};
}
}
}
throw new IllegalArgumentException("No two sum solution");
}
}複製程式碼
至於結果嘛,最簡單的自然也高效不到哪裡。
分析
這個方法的原理很簡單,就是將每一個值與其他的值迴圈遍歷,看是否有符合條件的情況發生。稍微要注意的是 for(int k=i+1;k<nums.length;k++) 這裡k=i+1是為了避免前面迴圈過的情況再次迴圈一遍。
時間複雜度 : O(n^2) 。n個元素都要迴圈遍歷陣列內其餘的元素,所以是n^2。
空間複雜度 : O(1) .
方法二:兩次迴圈的 hash table
public class Solution {
public int[] twoSum(int[] numbers, int target) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < numbers.length; i++) {
map.put(numbers[i],i);
}
for (int i = 0; i < numbers.length; i++) {
int requestNum = target - numbers[i];
if (map.containsKey(requestNum)&&map.get(requestNum)!=i) {
return new int[]{i,map.get(requestNum)};
}
}
throw new IllegalArgumentException("No two sum solution");
}
}複製程式碼
可以看到效率有了很大的提升。
分析
這個方法的原理其實就是使用hash table 來將時間成本來替換空間成本,將一次複雜度為O(n)的迴圈變為接近O(1)的查詢,為什麼是接近呢,因為如果hash table發生大量的碰撞,就會導致複雜度向O(n)靠近。我們將陣列裡每一個元素的值作為key存入hash table,而將其序列號作為對應的value存入hash table,然後遍歷陣列查詢是否有對應的值在hash table 的key中,有則取出該key對應的value。
時間複雜度 : O(n)
空間複雜度 : O(n)
方法三:一次迴圈的 hash table
public class Solution {
public int[] twoSum(int[] numbers, int target) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int i = 0; i < numbers.length; i++) {
int requestNum = target - numbers[i];
if (map.containsKey(requestNum)) {
return new int[]{map.get(requestNum),i};
}
map.put(numbers[i],i);
}
throw new IllegalArgumentException("No two sum solution");
}
}複製程式碼
效率稍微提高了一些。。但是有些不能接受啊。怎麼還只是在中間的位置。。。然後試了幾次,大概在40%到50%徘徊,前面那些是用什麼算的。。。為何那麼快。理論上來說至少需要迴圈比對結果一次,也就是需要O(n)的複雜度。
分析
這個方法的原理其實就是方法二的一個改善,因為我們不需要將全部的陣列都放入hash table ,我們最終的目的是為了得到兩個相加等於目標數的值的序號即可,所以我們在將陣列裡的值放入hash table 的時候就進行比對,一旦得到所需要的值立即結束迴圈。
時間複雜度 : O(n)
空間複雜度 : O(n)
如果你有更好的辦法或者對我這裡的描述有其他看法,請聯絡我。謝謝