算法訓練第七天 | 第三章 哈希表 454,383,15,18

代碼隨想錄算法訓練第七天,繼續(xù)哈希表

今日任務
● 454.四數(shù)相加II
● 383. 贖金信
● 15. 三數(shù)之和
● 18. 四數(shù)之和
● 總結(jié)

454. 四數(shù)相加II

給定四個包含整數(shù)的數(shù)組列表 A , B , C , D ,計算有多少個元組 (i, j, k, l) 裕坊,使得 A[i] + B[j] + C[k] + D[l] = 0制跟。

思路:
將四個數(shù)組 簡化成two sum 問題邻邮, 具體步驟:

  1. 首先定義 一個unordered_map,key放a和b兩數(shù)之和路媚,value 放a和b兩數(shù)之和出現(xiàn)的次數(shù)谐腰。
  2. 遍歷大A和大B數(shù)組杰捂,統(tǒng)計兩個數(shù)組元素之和翰灾,和出現(xiàn)的次數(shù)哮肚,放到map中停局。
  3. 定義int變量count很钓,用來統(tǒng)計 a+b+c+d = 0 出現(xiàn)的次數(shù)。
  4. 再遍歷大C和大D數(shù)組董栽,找到如果 0-(c+d) 在map中出現(xiàn)過的話码倦,就用count把map中key對應的value也就是出現(xiàn)次數(shù)統(tǒng)計出來。
  5. 最后返回統(tǒng)計值 count 就可以了
class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> adSum = new HashMap<Integer, Integer>();
        int resCount = 0;  // result
        //save sum of A, B, and occurrence
        for(int i = 0; i < nums1.length; i++){
            for(int j = 0; j < nums2.length; j++){
                int sum = nums1[i] + nums2[j];
                if(adSum.containsKey(sum)){
                    //put() will override old value
                    adSum.put(sum, adSum.get(sum) + 1);
                }else{
                    adSum.put(sum, 1);
                }
            }
        }
        //iterate C and D, find if 0-(c+d) exist in map 
        for(int i = 0; i < nums3.length; i++){
            for(int j = 0; j < nums4.length; j++){
                int sum = nums3[i] + nums4[j];
                int target = 0 - sum;
                if(adSum.containsKey(target)){
                    resCount += adSum.get(target);
                }
                
            }
        }
    return resCount;
            
    }
}

時間復雜度: O(nn)
空間復雜度: O(n
n) 哈希映射需要的空間锭碳, 最差情況下A[i] + B[j]值均不相同袁稽,需要n*n

  1. 贖金信
    給定一個贖金信 (ransom) 字符串和一個雜志(magazine)字符串,判斷第一個字符串 ransom 能不能由第二個字符串 magazines 里面的字符構(gòu)成擒抛。如果可以構(gòu)成推汽,返回 true ;否則返回 false歧沪。

(題目說明:為了不暴露贖金信字跡歹撒,要從雜志上搜索各個需要的字母,組成單詞來表達意思诊胞。雜志字符串中的每個字符只能在贖金信字符串中使用一次暖夭。)

你可以假設兩個字符串均只含有小寫字母。

本題 和 242.有效的字母異位詞 是一個思路
因為題目所只有小寫字母撵孤,那可以采用空間換取時間的哈希策略迈着, 用一個長度為26的數(shù)組還記錄magazine里字母出現(xiàn)的次數(shù)。

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        if(ransomNote.length() > magazine.length()) return false;
        
        int[] avaliableLetters = new int[26];

        //for(char c : magazine.toCharArray())
        for(int i = 0; i < magazine.length(); i++){
            
            avaliableLetters[magazine.charAt(i) - 'a']++;
        }
        
        for(int i = 0; i < ransomNote.length(); i++){
            int index = ransomNote.charAt(i) - 'a';
            avaliableLetters[index]--;
            if(avaliableLetters[index] < 0) return false;
            
        }
        return true;
    }
}

15. 三數(shù)之和

給你一個包含 n 個整數(shù)的數(shù)組 nums早直,判斷 nums 中是否存在三個元素 a寥假,b市框,c 霞扬,使得 a + b + c = 0 ?請你找出所有滿足條件且不重復的三元組枫振。

注意: 答案中不可以包含重復的三元組喻圃。

雙指針算法:
對數(shù)組進行排序
遍歷排序后數(shù)組:

  • 若 nums[i]>0:因為已經(jīng)排序好,所以后面不可能有三個數(shù)加和等于 0粪滤,直接返回結(jié)果斧拍。
  • 對于重復元素:跳過,避免出現(xiàn)重復解
  • 令左指針 L=i+1杖小,右指針 R=n?1肆汹,當 L<R 時愚墓,執(zhí)行循環(huán):
    -當 nums[i]+nums[L]+nums[R]==0,執(zhí)行循環(huán)昂勉,判斷左界和右界是否和下一位置重復浪册,去除重復解。并同時將 L,R 移到下一位置岗照,尋找新的解
    - 若和大于 0村象,說明 nums[R] 太大,R 左移
    - 若和小于 0攒至,說明 nums[L] 太小厚者,L 右移
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> resList = new ArrayList<>();
        //original order doesnt matter for return result 
        //sort the array, use left,right pointer to get sum of triplets
        Arrays.sort(nums);
        int n = nums.length;
        
        //if smallest number > 0 or largest number < 0, wont find any qualifying triplets
        if(nums[0] > 0 || nums[n - 1] < 0) return resList;
        
        for(int i = 0; i < n - 2; i++){
            //skip duplicates for first number
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            //left points to the smallest number besides i
            //right points to the largest number besides i
            int left = i + 1, right = n - 1;
            
            while(left < right){
                
                int sum = nums[i] + nums[left] + nums[right];
                if(sum > 0){
                    //nums[left] + nums[right] needs to be smaller to get sum to 0
                    right--;
                }else if(sum < 0){
                    //nums[left] + nums[right] needs to be bigger to get a 0 sum
                    left++;
                }else{
                    //triplet found
                    resList.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    //skip duplicates
                    while(left < right && nums[left] == nums[left + 1]) left++;
                    while(left < right && nums[right] == nums[right - 1]) right--;
                    //increment to process next combination
                    left++;
                    right--;
                }
            }
            
            
        } 
        return resList;
    }
}

時間復雜度 O(n * n) : 數(shù)組排序O(nlogn); 固定指針i 循環(huán)復雜度 O(N), 雙指針遍歷O(N)
空間復雜度O(1)

18. 四數(shù)之和

對于15.三數(shù)之和 (opens new window)雙指針法就是將原本暴力O(n3)的解法,降為O(n2)的解法迫吐,四數(shù)之和的雙指針解法就是將原本暴力O(n4)的解法库菲,降為O(n3)的解法。

哈希表的經(jīng)典題目:454.四數(shù)相加II (opens new window)志膀,相對于本題簡單很多蝙昙,因為本題是要求在一個集合中找出四個數(shù)相加等于target,同時四元組不能重復梧却。而454是四個獨立的數(shù)組奇颠,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考慮有重復的四個元素相加等于0的情況放航。所以可以利用hashmap存儲A[i] + B[j]之和烈拒,將之變形為O(n*n) 解法

思路:

四數(shù)之和與前面三數(shù)之和的思路幾乎是一樣的,
其實就是在三數(shù)之和的基礎上多添加一個遍歷的指針而已广鳍。

  • 使用四個指針(i<j<left<right)荆几。固定最小的a和b在左邊,c=b+1,d=_size-1 移動兩個指針包夾求解赊时。
    保存使得nums[i]+nums[j]+nums[left]+nums[right]==target的解吨铸。偏大時d左移,偏小時c右移祖秒。
    left和right相遇時诞吱,表示以當前的i和j為最小值的解已經(jīng)全部求得。j++,進入下一輪循環(huán)j循環(huán)竭缝,當j循環(huán)結(jié)束后房维。 i++,進入下一輪i循環(huán)抬纸。 即(i在最外層循環(huán)咙俩,里面嵌套j循環(huán),再嵌套雙指針left,right包夾求解)湿故。

a遍歷O(N)里嵌套b遍歷O(N)再嵌套c,d雙指針O(N)--> O(N^3)阿趁。 比暴力法O(N^4)好些吧膜蛔。

但是有一些細節(jié)需要注意,例如: 不要判斷nums[k] > target 就返回了脖阵,三數(shù)之和 可以通過 nums[i] > 0 就返回了飞几,因為 0 已經(jīng)是確定的數(shù)了,四數(shù)之和這道題目 target是任意值独撇。比如:數(shù)組是[-4, -3, -2, -1]屑墨,target是-10,不能因為-4 > -10而跳過纷铣。

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> resList = new ArrayList<>();
        Arrays.sort(nums);
        int n = nums.length;
        if(n < 4) return resList;
        
        int left,right;
        long sum;
        for(int i = 0; i < n - 3; i++){
            //remove duplicate
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            
            for(int j = i + 1; j < n - 2; j++){
                //remove duplicate, notice j lower bound value
                if( j > i + 1 && nums[j] == nums[j - 1]) continue;
                left = j + 1;
                right = n - 1;
                
                while(left < right){
                    sum = (long)nums[i] + nums[j] + nums[left] + nums[right];
                    if(sum < target){
                        left++;
                    }else if(sum > target){
                        right--;
                    }else{
                        //quadruplets found
                        resList.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        while(left < right && nums[left] == nums[left + 1]) left++;
                        while(left < right && nums[right] == nums[right-1]) right--;
                        left++;
                        right--;
                    }
                }
            
            }
        }
        return resList;
    }
}

總結(jié):
注意三種哈希結(jié)構(gòu)的不同應用場景

  • 數(shù)組:
    242.有效的字母異位詞 (opens new window)是求 字符串a(chǎn) 和 字符串b 是否可以相互組成卵史,在383.贖金信 (opens new window)中是求字符串a(chǎn)能否組成字符串b,而不用管字符串b 能不能組成字符串a(chǎn)搜立。
    使用map的空間消耗要比數(shù)組大一些以躯,因為map要維護紅黑樹或者符號表,而且還要做哈希函數(shù)的運算啄踊。所以數(shù)組更加簡單直接有效忧设!

  • set(集合)
    數(shù)組的大小是有限的,受到系統(tǒng)椀咄ǎ空間(不是數(shù)據(jù)結(jié)構(gòu)的棧)的限制址晕。
    如果數(shù)組空間夠大,但哈希值比較少顿锰、特別分散谨垃、跨度非常大,使用數(shù)組就造成空間的極大浪費硼控。更適合set

  • map(映射) store <key, value> pair
    使用用例: 1.兩數(shù)之和刘陶, 454.四數(shù)相加
    使用數(shù)組和set來做哈希法的局限:
    數(shù)組的大小是受限制的,而且如果元素很少牢撼,而哈希值太大會造成內(nèi)存空間的浪費匙隔。
    set是一個集合,里面放的元素只能是一個key熏版,而兩數(shù)之和這道題目纷责,不僅要判斷y是否存在而且還要記錄y的下標位置,因為要返回x 和 y的下標纳决。所以set 也不能用碰逸。
    map是一種<key, value>的結(jié)構(gòu),本題可以用key保存數(shù)值阔加,用value在保存數(shù)值所在的下標。所以使用map最為合適满钟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胜榔,一起剝皮案震驚了整個濱河市胳喷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夭织,老刑警劉巖吭露,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異尊惰,居然都是意外死亡讲竿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門弄屡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來题禀,“玉大人,你說我怎么就攤上這事膀捷÷踵冢” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵全庸,是天一觀的道長秀仲。 經(jīng)常有香客問我,道長壶笼,這世上最難降的妖魔是什么神僵? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮覆劈,結(jié)果婚禮上挑豌,老公的妹妹穿的比我還像新娘。我一直安慰自己墩崩,他們只是感情好氓英,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鹦筹,像睡著了一般铝阐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上铐拐,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天徘键,我揣著相機與錄音,去河邊找鬼遍蟋。 笑死吹害,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的虚青。 我是一名探鬼主播它呀,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纵穿?” 一聲冷哼從身側(cè)響起下隧,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谓媒,沒想到半個月后淆院,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡句惯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年土辩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抢野。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡拷淘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蒙保,到底是詐尸還是另有隱情辕棚,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布邓厕,位于F島的核電站逝嚎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏详恼。R本人自食惡果不足惜补君,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望昧互。 院中可真熱鬧挽铁,春花似錦、人聲如沸敞掘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽玖雁。三九已至更扁,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赫冬,已是汗流浹背浓镜。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留劲厌,地道東北人膛薛。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像补鼻,于是被迫代替她去往敵國和親哄啄。 傳聞我的和親對象是個殘疾皇子雅任,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容