前言
我從兩周前開始接觸算法,從一周前開始接觸leetcode。我主要使用leetcode練習算法題目,本沒有記錄一番的打算,正巧加入了耗子叔的左耳聽風 ARTS專欄,其中的A就是每周一道leetcode算法題,并且需要記錄下來分享,于是開始了leetcode做題記錄的第一篇。
在文章中我不會只是簡單記錄答案,我會寫下自己解題思路的變化,同時也會對復雜的代碼處進行解釋,因為剛開始接觸算法題目,所以從easy級別的開始,有復雜代碼的可能性比較低,接下來直接上題目。
題目 兩數之和 題號#1
查看題目可點擊此處。
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和為目標值的那兩個 整數,并返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,你不能重復利用這個數組中同樣的元素。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解題思路
讀懂題目的第一思路就是利用兩個循環對數組的元素進行加和,與target比對判斷,成功就返回數組兩個元素下標,代碼如下:
class Solution {
public int[] twoSum(int[] nums, int target) {
int i = 0, j = i + 1;
for(; i < nums.length - 1; i++) {
for(j = i + 1; j < nums.length; j++) {
if(nums[j] + nums[i] == target) {
return new int[] {i, j};
}
}
}
return null;
}
}
這段代碼時間復雜度是O(n2),應該還有更優的解。轉換思路,其實結果是返回滿足條件的元素下標,元素與下標需要關聯上,又可以快速通過元素返回下標的方式讓我想到了Map。代碼如下:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>(nums.length);
for(int i = 0; i < nums.length && nums[i] <= target; i++) {
map.put(nums[i], i);
}
for(Map.Entry<Integer, Integer> entry : map.entrySet()) {
Integer index = map.get(target - entry.getKey());
if(index != null) {
return new int[] {entry.getValue(), index};
}
}
return null;
}
}
滿心歡喜以為找到了O(n)時間復雜度的解法,結果這個提交失敗,失敗示例如下
輸入:
[3,3]
6
輸出:
[1,1]
預期:
[0,1]
究其原因,數組中的元素重復,Map中的key產生覆蓋,而又使用Map進行遍歷,無法遍歷到數組所有元素,并且未判斷是否重復使用一個數組元素進行加和滿足條件,導致出現此問題。于是繼續改進代碼:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>(nums.length / 3);
for(int i = 0; i < nums.length && nums[i] <= target; i++) {
map.put(nums[i], i);
}
for(int i = 0; i < nums.length; i++) {
int otherNum = target - nums[i];
if(map.containsKey(otherNum) && map.get(otherNum) != i) {
return new int[] {i, map.get(otherNum)};
}
}
return null;
}
}
將遍歷map修改為遍歷數組本身,雖然還是會有數組元素重復導致key覆蓋,但是只要能找出一對滿足加和為target條件的元素下標即可,所以map的key因重復數組元素導致覆蓋也就不影響結果,并且在第10行代碼上加入了對是否重復使用一個數組元素進行加和的判斷。其實,這段代碼也失敗了,失敗樣例如下:
輸入:
[-1,-2,-3,-4,-5]
-8
輸出:
[0,0]
預期:
[2,4]
原因是第4行代碼處增加了nums[i] <= target的判斷,我認為相加之和等于target的兩數必然小于或等于target,想利用些條件減少map中的數據量,卻忽略了負數的可能性。根據問題修改代碼:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>(nums.length / 3);
for(int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for(int i = 0; i < nums.length; i++) {
int otherNum = target - nums[i];
if(map.containsKey(otherNum) && map.get(otherNum) != i) {
return new int[] {i, map.get(otherNum)};
}
}
return null;
}
}
這段代碼可以成功通過所有測試樣例,而且時間也比雙循環時提高了許多。
根據官方還有一個更優的解,就是在循環的同時對map進行判斷,不滿足條件便放入map,滿足條件就返回結果。代碼如下:
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>(nums.length / 3);
for(int i = 0; i < nums.length; i++) {
int otherNum = target - nums[i];
if(map.containsKey(otherNum)) {
return new int[] {map.get(otherNum), i};
}
map.put(nums[i], i);
}
return null;
}
}
通過幾天的解題發現自己對題目樣例中可能出現的特殊情況缺少考慮,對操作的簡化也還需要更深入的思考。