1. 程式人生 > >【LeetCode】雜湊表 hash_table(共88題)

【LeetCode】雜湊表 hash_table(共88題)

【1】Two Sum (2018年11月9日,k-sum專題,演算法群衍生題)

給了一個數組 nums, 和一個 target 數字,要求返回一個下標的 pair, 使得這兩個元素相加等於 target 。

題解:我這次最大範圍的優化程式碼, hash-table + one pass,時間複雜度 O(N),空間複雜度 O(N)。重點在於動態找,一邊生成hash-table的同時去找答案,不是先生成hash-table再開始找答案。

 1 //這種類似 hash 的能用 unordered_map 就不要用 map, hash-table + one pass
 2 class Solution {
3 public: 4 vector<int> twoSum(vector<int>& nums, int target) { 5 const int n = nums.size(); 6 unordered_map<int, int> mp; 7 vector<int> ans(2); 8 for (int i = 0; i < n; ++i) { 9 int key = target - nums[i]; 10 if
(mp.find(key) != mp.end()) { 11 ans[0] = mp[key]; 12 ans[1] = i; 13 break; 14 } 15 mp[nums[i]] = i; 16 } 17 return ans; 18 } 19 };
View Code

【3】Longest Substring Without Repeating Characters

【18】4Sum (2018年11月9日,k-sum專題,演算法群衍生題)

【30】Substring with Concatenation of All Words

【36】Valid Sudoku

【37】Sudoku Solver

【49】Group Anagrams

【76】Minimum Window Substring

【85】Maximal Rectangle

【94】Binary Tree Inorder Traversal

【136】Single Number

【138】Copy List with Random Pointer

【149】Max Points on a Line (2018年11月10號,演算法群)

給了 2D 平面上的 n 個點,兩個點能組成一條直線,返回一條直線上最多能有幾個點。

【159】Longest Substring with At Most Two Distinct Characters

【166】Fraction to Recurring Decimal

【170】Two Sum III - Data structure design

【187】Repeated DNA Sequences

【202】Happy Number

【204】Count Primes

【205】Isomorphic Strings

【217】Contains Duplicate

【219】Contains Duplicate II

【242】Valid Anagram

【244】Shortest Word Distance II

【246】Strobogrammatic Number

給了一個字串代表一個數字,返回這個數字 upside down 之後是不是和原來數字一樣。(返回布林型別)

Example 1:
Input:  "69"
Output: true

Example 2:
Input:  "88"
Output: true

Example 3:
Input:  "962"
Output: false

題解:用個 map 表示數字字元上下顛倒後的對應數字字元。務必寫全:mp['0'] = '0', mp['1'] = '1', mp['6'] = '9', mp['8'] = '8', mp['9'] = '6';

 1 class Solution {
 2 public:
 3     bool isStrobogrammatic(string num) {
 4         map<char, char> mp;
 5         mp['0'] = '0', mp['1'] = '1', mp['6'] = '9', mp['8'] = '8', mp['9'] = '6';
 6         string temp = num;
 7         reverse(temp.begin(), temp.end());
 8         for (auto& p : temp) {
 9             if (mp.find(p) == mp.end()) {return false;}
10             p = mp[p];
11         }
12         return temp == num;
13     }
14 };
View Code

【249】Group Shifted Strings

【266】Palindrome Permutation

【274】H-Index

【288】Unique Word Abbreviation

【290】Word Pattern

【299】Bulls and Cows

【311】Sparse Matrix Multiplication

【314】Binary Tree Vertical Order Traversal

【325】Maximum Size Subarray Sum Equals k

【336】Palindrome Pairs

【340】Longest Substring with At Most K Distinct Characters

【347】Top K Frequent Elements

【349】Intersection of Two Arrays (2018年11月6日,演算法群相關題)

給了兩個陣列,返回他們交疊的元素,如果有重複元素的話,只返回一個就行了。

題解:直接用 set 解了。

 1 //題意是給了兩個陣列,返回他們的重複元素.
 2 //我是用了兩個set去重,然後O(n) 的時間複雜度遍歷出結果。
 3 class Solution {
 4 public:
 5     vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
 6         set<int> st1(nums1.begin(), nums1.end()), st2(nums2.begin(), nums2.end());
 7         vector<int> ans;
 8         for (auto num : st1) {
 9             if (st2.find(num) != st2.end()) {
10                 ans.push_back(num);
11             }
12         }
13         return ans;
14     }
15 };
View Code

【350】Intersection of Two Arrays II (2018年11月6日,演算法群)

給了兩個陣列,返回他們所有交疊的元素,元素可以任意順序返回,但是如果一個元素在A,B陣列中都出現多次,需要返回公共的多次。

Example 1:
Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2,2]

Example 2:
Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
Output: [4,9]

題解:用兩個map記錄每個陣列的元素和元素個數,然後遍歷一個map,對於兩個陣列都存在的元素t, 在ret數組裡面插入 min(cnt1[t], cnt2[t]). 時間複雜度是 O(N),肯定是線性的。

 1 class Solution {
 2 public:
 3     vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
 4         vector<int> ans;
 5         cnt1 = genMap(nums1);
 6         cnt2 = genMap(nums2);
 7         for (auto p : cnt1) {
 8             int t = p.first, times = min(cnt1[t], cnt2[t]);
 9             for (int i = 0; i < times; ++i) {
10                 ans.push_back(t);
11             }
12         }
13         return ans;
14     }
15     map<int, int> genMap(vector<int>& nums) {
16         map<int, int> ret;
17         for (auto p : nums) {
18             ret[p]++;
19         }
20         return ret;
21     }
22     map<int, int> cnt1, cnt2;
23 };
View Code

還有一種方法就是用元素比較少的陣列建立map,然後O(N)的遍歷另外一個數組。

 1 class Solution {
 2 public:
 3     vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
 4         const int n = nums1.size(), m = nums2.size();
 5         map<int, int> cnt =  n <= m ? genMap(nums1) : genMap(nums2);
 6         vector<int> ans = n > m ? genAns(nums1, cnt) : genAns(nums2, cnt);
 7         return ans;
 8     }
 9     map<int, int> genMap(const vector<int>& nums) {
10         map<int, int> ret;
11         for (auto p : nums) {
12             ret[p]++;
13         }
14         return ret;
15     }
16     vector<int> genAns(const vector<int>& nums, map<int, int>& cnt) {
17         vector<int> ans;
18         for (auto p : nums) {
19             if (cnt.find(p) != cnt.end() && cnt[p] > 0) {
20                 cnt[p]--;
21                 ans.push_back(p);
22             } 
23         }
24         return ans;
25     }
26 };
View Code

本題還有三個follow up:

(1)What if the given array is already sorted? How would you optimize your algorithm?

如果陣列已經排好序了,我就不用map了,每個陣列用一個指標遍歷,相同元素就加到ans陣列中。時間複雜度O(N + M),省掉了map。

(2)What if nums1's size is small compared to nums2's size? Which algorithm is better?

 如果nums1的size很小,那我就用nums1建立map,然後遍歷nums2,往答案數組裡面加元素。

(3)What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the memory at once?

nums2用資料流讀出來(或者 read chunks of array that fit the memory),nums1計算一個map出來,每次nums2中有個元素在map1中對應,就把map1[x]--。

discuss:如果兩個array都巨大,memory讀不出來,那就先external sort indivisually,每次從排序好的數組裡面讀出來兩個元素,交疊。就和 follow-up1 一樣。

【355】Design Twitter

【356】Line Reflection

【358】Rearrange String k Distance Apart

【359】Logger Rate Limiter

【380】Insert Delete GetRandom O(1)

【381】Insert Delete GetRandom O(1) - Duplicates allowed

【387】First Unique Character in a String

【389】Find the Difference

【409】Longest Palindrome (2018年11月14日,為了衝題數做的簡單題)

給了一個字串包含大小寫的英文字母(區分大小寫,相當於兩個字母),問用這個字串裡面的這些字母最長能組成多長的迴文串。

題解:我用了一個 map 記錄每個字母出現了幾次,如果出現了偶數次就直接加次數,出現了奇數次就加奇數次減一。此外,如果有奇數次的話, 最後答案要加1,因為那個字母可以放中心。

 1 class Solution {
 2 public:
 3     int longestPalindrome(string s) {
 4         const int n = s.size();
 5         unordered_map<int, int> mp;
 6         for (auto c : s) { mp[c]++; }
 7         int ret = 0;
 8         int add1 = 0;
 9         for (auto p : mp) {
10             if (p.second % 2) {
11                 add1 = 1;
12                 ret += p.second - 1;
13             } else {
14                 ret += p.second;
15             }
16         }
17         ret += add1;
18         return ret;
19     }
20 };
View Code

【438】Find All Anagrams in a String

【447】Number of Boomerangs

【451】Sort Characters By Frequency (2018年11月14日,為了衝刺題數)

給了一個字串 S, 要求給它重新排序,排序的規則是出現次數多的字母堆在一起放前面。

題解:無,我是用了兩個map。(有沒有更優解不知道,有點不用腦子做題了快)

 1 class Solution {
 2 public:
 3     string frequencySort(string s) {
 4         int n = s.size();
 5         unordered_map<char, int> mp;
 6         for (auto c : s) {
 7             mp[c]++;
 8         }
 9         map<int, vector<char>> mp2;
10         for (auto p : mp) {
11             mp2[p.second].push_back(p.first);
12         }
13         string ret = "";
14         for (auto p : mp2) {
15             int cnt = p.first; vector<char> vec = p.second;
16             for (auto e : vec) {
17                 ret = string(cnt, e) + ret;
18             }
19         }
20         return ret;
21     }
22 };
View Code

【454】4Sum II (2018年11月9日,演算法群)

給了四個長度一樣的陣列A,B,C,D,問從這四個陣列中每個陣列都選出一個數,這四個數加起來為 0 的種數一共有多少種。

題解:早上無聊看了qc的極客時間,他也就總結了兩種有效的方法對於【15】3 Sum這種題型。具體可以看 陣列 或者 2 pointers 的專題。不過我覺著都一樣吧。

本題就是 hash, 把A,B陣列任意兩個數的和存在一個 unordered_map 裡面,然後遍歷 C,D 陣列,選擇兩個元素相加,看在 hash 裡面能不能找到這兩個數和的相反數。時間複雜度是 O(N^2)。但是需要注意的是 map 這題要 200+ms, unordered_map  只需要 100+ ms。 

 1 //unordered_map beats 90%+, map beats 30%+
 2 class Solution {
 3 public:
 4     int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
 5         unordered_map<int, int> mapAB = genMap(A, B);
 6         int ans = 0;
 7         for (int i = 0; i < C.size(); ++i) {
 8             for (int j = 0; j < D.size(); ++j) {
 9                 int value = C[i] + D[j];
10                 if (mapAB.find(-value) != mapAB.end()) {
11                     ans += mapAB[-value];
12                 }
13             }
14         }
15         return ans;
16     }
17     unordered_map<int, int> genMap(const vector<int>& A, const vector<int>& B) {
18         unordered_map<int, int> cnt;
19         for (int i = 0; i < A.size(); ++i) {
20             for (int j = 0; j < B.size(); ++j) {
21                 int summ = A[i] + B[j];
22                 cnt[summ]++;
23             }
24         }
25         return cnt;
26     }
27     
28 };
View Code

【463】Island Perimeter (計算島的周長)(2018年11月13日,為了衝點題數做的)

給了一個二維的grid,裡面是 0/ 1 矩陣, 0代表水,1代表陸地。一個小方塊的邊長是 1。 問陸地組成的島的周長是多少。

Input:
[[0,1,0,0],
 [1,1,1,0],
 [0,1,0,0],
 [1,1,0,0]]

Output: 16
Explanation: The perimeter is the 16 yellow stripes in the image below:

題解:我直接二維陣列遍歷了,沒用搜索。

 1 class Solution {
 2 public:
 3     int islandPerimeter(vector<vector<int>>& grid) {
 4         int ret = 0;
 5         int n = grid.size(), m = grid[0].size();
 6         for (int i = 0; i < n; ++i) {
 7             for (int j = 0; j < m; ++j) {
 8                 int temp = 0;
 9                 if (grid[i][j] == 1) {
10                     if (i == 0) {temp += 1;}
11                     if (i == n - 1) {temp += 1;}
12                     if (j == 0) {temp += 1;}
13                     if (j == m - 1) {temp += 1;}
14                     for (int k = 0; k < 4; ++k) {
15                         int newx = i + dirx[k], newy = j + diry[k];
16                         if (newx < 0 || newx >= n || newy < 0 || newy >= m) {continue;}
17                         if (grid[newx][newy] == 0) {temp += 1;}
18                     }
19                 }
20                 ret += temp;
21             }
22         }
23         return ret;
24     }
25     int dirx[4] = {-1, 0, 1, 0};
26     int diry[4] = {0, -1, 0, 1};
27     
28 };
View Code

【500】Keyboard Row

【508】Most Frequent Subtree Sum

【525】Contiguous Array

【535】Encode and Decode TinyURL

【554】Brick Wall

【560】Subarray Sum Equals K

【575】Distribute Candies

【594】Longest Harmonious Subsequence

【599】Minimum Index Sum of Two Lists

【609】Find Duplicate File in System

【624】Maximum Distance in Arrays

【632】Smallest Range

【645】Set Mismatch

【648】Replace Words

【676】Implement Magic Dictionary

【690】Employee Importance

【692】Top K Frequent Words

【694】Number of Distinct Islands

【705】Design HashSet

【706】Design HashMap

【710】Random Pick with Blacklist

【711】Number of Distinct Islands II

【718】Maximum Length of Repeated Subarray

【720】Longest Word in Dictionary

【726】Number of Atoms

【734】Sentence Similarity

【739】Daily Temperatures

【748】Shortest Completing Word

【760】Find Anagram Mappings

【770】Basic Calculator IV

【771】Jewels and Stones

【781】Rabbits in Forest

【811】Subdomain Visit Count

【846】Hand of Straights(不好意思,本來是map分類,然鵝map分類一共就兩個題,我就放在了這裡orz)

小紅有一堆紙牌,每張紙牌上有一個數字,問能不能把紙牌均勻的分成每組 W 張,每組的數字都是連續遞增 1 的。

Example 1:
Input: hand = [1,2,3,6,2,3,4,7,8], W = 3
Output: true
Explanation: Alice's hand can be rearranged as [1,2,3],[2,3,4],[6,7,8].

Example 2:
Input: hand = [1,2,3,4,5], W = 4
Output: false
Explanation: Alice's hand can't be rearranged into groups of 4.

1 <= hand.length <= 10000
0 <= hand[i] <= 10^9
1 <= W <= hand.length

題解:我本來想排序之後二分的,但是點了下tag,tag 寫的是 map, 我就往 map 上面想了。用了 map 能 AC 但是時間上比較差。還要看別人的題解,要記得看discuss。

 1 class Solution {
 2 public:
 3     bool isNStraightHand(vector<int>& hand, int W) {
 4         const int n = hand.size();
 5         if (n % W != 0) {return false;}
 6         map<int, int> mp;
 7         for (auto p : hand) {
 8             mp[p]++;
 9         }
10         int cnt(0);
11         while (cnt < n / W) {
12             int begin;
13             for (auto ele : mp) {
14                 if (ele.second > 0) {
15                     begin = ele.first;
16                     break;
17                 }
18             }
19             int num = begin; 
20             for (; num < begin + W; ++num) {
21                 if (mp.find(num) == mp.end() || mp[num] <= 0) {
22                     return false;
23                 }
24                 mp[num]--;
25             }
26             cnt++;
27         }
28         return true;
29     }
30 };
View Code

【884】Uncommon Words from Two Sentences

【895】Maximum Frequency Stack