初衷
先說一下初衷吧,這次找工作深深感受到不刷題的被動。即便你自己認爲自己的實力沒有問題,遇到問題花費一定時間也能解決,但是面試的時候就那麼幾道題,如果你慢慢想,時間根本來不及。我也在面試的時候自己做出了動態規劃的題,但是面對一些變種或者實際寫碼過程中可能會有很多細節問題需要考慮,比如用什麼數據結構進行緩存、邊界值怎麼處理等問題。一旦出現卡頓,面試官可能就會認爲你的寫碼能力不足,這個東西百口莫辯,畢竟事實擺在那裏。
而且,如果到準備找工作的時候纔開始刷題,時間根本來不及,所以乾脆想做個長期計劃,每週刷一道題,只做簡單和中等。一年也能刷五十多道題了。每道題寫個博客,做個記錄,也能保證刷題更高效,因爲輸出的時候可能纔會發現很多點還有缺陷。
第一題
這道題很簡單,但是也有不少細節點可以注意的。
題目:
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,數組中同一個元素不能使用兩遍。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因爲 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/two-sum
我覺得做題首先要從最簡單的方法開始入手,然後逐步優化。(當然熟練之後其實就可以從更高級別開始入手了)
這道題解法很多。
解法一:暴力法
直接遍歷數組,對每個元素計算能湊成target的數字大小,然後在數組中查找。
這個方法時間複雜度O(),空間複雜度O(1)
這個方法過於暴力和簡單,代碼不做實現。
解法二:先排序
這是一個優化方向。對數組排序,利用有序的特性,可以做一些優化。
排序後,可以用雙指針,從數組頭部和尾部向中間推進。如果當前二元素之和等於target,就返回這兩個指針;如果大於target,尾指針減1;如果小於target,頭指針加1。
排序時間複雜度爲O(),雙指針遍歷時間複雜度O(n),總的時間複雜度爲O()
空間複雜度爲O(1)。
解法三:HashMap緩存,兩次遍歷
這是一個空間換時間的方法。將數組中的元素保存在HashMap裏,元素值作key,下標做value。
直觀方法是兩次遍歷,第一次遍歷緩存,第二次遍歷查找。
思路不難,直接上代碼,一個需要注意的點是同一個下標不能使用兩次。(但是題目裏其實沒有說數組中的元素都不相等,其實這裏是有可能出現衝突的,那麼HashMap裏保存的應該是List纔對,但實際上這個代碼也能ac,就懶得寫了,而且下一個優化方法能解決這個問題)
import java.util.HashMap;
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int toFind = target - nums[i];
if (map.containsKey(toFind) && map.get(toFind) != i) {
return new int[] {i, map.get(toFind)};
}
}
return null;
}
}
解法三:HashMap緩存,一次遍歷
這個方法確實沒想到,看了官方解法才知道還能做進一步優化。厲害!
其實思路很簡單。在一次遍歷過程中,其實就能邊遍歷邊使用已有的歷史信息了。只要當前正在訪問的這個元素能和以前已經遍歷到的某個元素組成target,那麼就可以直接返回了。而且這樣一次遍歷完成之後,一定是能確保找到答案的(這個可能要證明?沒想到簡單的證明方法,根據推理是可以知道的,不展開了)
代碼如下,這個還不需要考慮用到的索引是相同的,因爲當前訪問的元素是在進行target判斷之後才放入HashMap中的,不會重複。
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap();
for (int i = 0; i < nums.length; i++) {
int toFind = target - nums[i];
if (map.containsKey(toFind)) {
return new int[] {i, map.get(toFind)};
}
// 在target查找之後再放入map,避免重複使用本元素。
// 那麼,如果可以重複使用的場景,是不是可以把它挪到前面實現重複使用呢?
map.put(nums[i], i);
}
return null;
}
}
耗時:
從開始做題到寫完文章,消耗了兩個番茄鍾(每個25分鐘)。看來這個事情還是挺耗時的,這還是簡單的題目,就已經花費了這麼長的時間,做個記錄,有個心理預期和時間估算。