STL 1. vector 會自動allocate memory的array example vector<int> vec; vec.push_back(2); cout << vec[0] << endl; //2 int lastElement = vec.back(); //2 vec.pop_back(); //vec become empty 可以一開始先allocate好一些空間, 並給予init value vector<int> initWithOne(10, 1); //size = 10, value = 1 也可以是nested vector<vector<int>> twoDimVec; 應用: 存Graph 例如說有一堆edge存在 vector\<int\> edges, 以及node數量n, 我們可以轉成Adj list void SomeGraphProblem(vector<vector<int>> edges, int n) { vector<vector<int>> Graph(n); //allocate n vectors for(auto edge : edges) { //Assume graph is undirected Graph[edge[0]].push_back(edge[1]); Graph[edge[1]].push_back(edge[0]); } } STL的element通常都可以混合使用, 比如說 vector<unordered_map<int, int>> hashMapVec(n); 應用: DP, 當state不一定都在整數, 或者數字可能過大 https://leetcode.com/problems/target-sum/description/ class Solution { public: vector<unordered_map<int, int>> index2Target2Ans; int CountWays(vector<int>& nums, int target, int curIndex) { int n = nums.size(); if(curIndex == n && target == 0) { return 1; } if(curIndex == n) { return 0; } if(index2Target2Ans[curIndex].find(target) != index2Target2Ans[curIndex].end()) { return index2Target2Ans[curIndex][target]; } int plus = target - nums[curIndex]; int minus = target + nums[curIndex]; return index2Target2Ans[curIndex][target] = (CountWays(nums, plus, curIndex + 1) + CountWays(nums, minus, curIndex + 1)); } int findTargetSumWays(vector<int>& nums, int target) { index2Target2Ans.resize(nums.size()); return CountWays(nums, target, 0); } }; 2. queue 宣告, 同樣可塞其他STL/Type queue<int> intQueue; queue<pair<int,int>> pairQueue; queue<vector<int>> vecQueue; queue<TreeNode*> treeNodeQueue; Member function queue<int> intQueue; intQueue.push(1); intQueue.push(2); intQueue.push(3); while(!intQueue.empty()) //intQueue.size() > 0 { int cur = intQueue.front(); intQueue.pop(); cout << cur ; } // 1, 2, 3 應用: BFS example1. BFS on tree https://leetcode.com/problems/binary-tree-level-order-traversal/ /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> leverOrderResult; if(!root) { return leverOrderResult; } queue<TreeNode*> NodeQueue; NodeQueue.push(root); while(!NodeQueue.empty()) { queue<TreeNode*> nextRoundQueue; vector<int> curLevel; while(!NodeQueue.empty()) { TreeNode* cur = NodeQueue.front(); NodeQueue.pop(); curLevel.push_back(cur->val); if(cur->left) { nextRoundQueue.push(cur->left); } if(cur->right) { nextRoundQueue.push(cur->right); } } leverOrderResult.push_back(curLevel); NodeQueue = nextRoundQueue; } return leverOrderResult; } }; example2. BFS on graph/state https://leetcode.com/problems/keys-and-rooms/ class Solution { public: bool canVisitAllRooms(vector<vector<int>>& rooms) { int n = rooms.size(); vector<vector<int>> graph(n); for(int i = 0 ; i < n ; i++) { vector<int> &keys = rooms[i]; for(int key : keys) { graph[i].push_back(key); } } queue<int> roomQueue; roomQueue.push(0); vector<bool> visted(n, false); visted[0] = true; int roomRemain = n; while(!roomQueue.empty()) { int curRoom = roomQueue.front(); roomQueue.pop(); roomRemain --; for(int nextRoom : graph[curRoom]) { if(!visted[nextRoom]) { visted[nextRoom] = true; roomQueue.push(nextRoom); } } } return roomRemain == 0; } }; 3. stack 宣告 stack<int> intStack; stack<pair<int,int>> pairStack; Member function stack<int> intStack; intStack.push(1); intStack.push(2); intStack.push(3); while(!intStack.empty()) { int cur = intStack.top(); intStack.pop(); cout << cur; } //3 2 1 應用:monotonic stack 在一個array中, 往左/右找第一個比自己大/小的 以Next Greater Element I為例, 題目要求: 對於每個nums1的數, 找到他在nums2裡面, 第一個往右找比他大的數 我們可以用一個hash map: unordered_map<int, int> num2NextGreater來存在nums2中 每個數, 第一個往右比較更大的數字為何 比如說nums2 = [1,3,4,2] 1往右比第一個比他大的是3 3往右比第一個比他大的是4 4往右比找不到比他大的數字, 依照題目要求, 答案為-1 2有右比找不到更大, 答案為-1 我們可以maintain一個stack, 從array的右邊開始掃, 如果目前stack最上方的數字 比目前看到的數字小, 則我們從stack pop該數字, 最後再push目前的數字 跑一下上面的範例 i = 3, original stack = [] , updated stack = [2] i = 2, original stack = [2], stack = [4] i = 1, original stack = [4], 此時在我們發現3無法讓4 pop出去, 這代表從右掃過來, 4是第一個比3大的數字, stack = [4,3] i = 0, original stack = [4,3], 此時我們發現1無法讓3 pop出去, 這代表從右邊掃過來, 3是第一個比1大的數字 stack = [4,3,1] 我們發現最後stack的內容是單調的 https://leetcode.com/problems/next-greater-element-i/ class Solution { public: vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) { unordered_map<int, int> num2NextGreater; stack<int> monoStack; int n = nums2.size(); for(int i = n - 1 ; i >= 0 ; i --) { while(!monoStack.empty() && nums2[i] > monoStack.top() ) { monoStack.pop(); } if(!monoStack.empty()) { num2NextGreater[nums2[i]] = monoStack.top(); } else { num2NextGreater[nums2[i]] = -1; } monoStack.push(nums2[i]); } vector<int> ret(nums1.size()); for(int i = 0; i < nums1.size() ; i++) { ret[i] = num2NextGreater[nums1[i]]; } return ret; } }; 4. unordered_map/map unordered_map: implementation為hash, average O(1), key必須能算hash map: implementation為red-black tree, O(logn), key必須能比大小 map的用法會跟vector/array類似, 都是用operator [] , 但vector/array的[]代表offset, map的[]代表access/update某個key 宣告 unordered_map<int, int> num2num; unordered_map<char, int> char2num; unordered_map<string, int> string2num; unordered_map<int, string> num2string; Member function num2string[5] = "abcdefg"; num2num[0] = 5; if(num2num.find(2) == num2num.end()) { cout << "Key 2 not exist\n"; } if(num2num.find(5) == num2num.end()) { cout << "Key 5 not exist\n"; } //Will NOT output this line, find based on key num2num.erase(2) // Remove key 2 from map 應用: DP存state (參考vector) 應用: 用string當成key https://leetcode.com/problems/group-anagrams/ class Solution { public: vector<vector<string>> groupAnagrams(vector<string>& strs) { unordered_map<string, vector<string>> ana2Group; for(string &str : strs) { string tmp = str; sort(tmp.begin(), tmp.end()); ana2Group[tmp].push_back(str); } vector<vector<string>> ret; for(auto it : ana2Group) { ret.push_back(it.second); } return ret; } }; 應用: 用char當成key https://leetcode.com/problems/longest-substring-without-repeating-characters/ class Solution { public: int lengthOfLongestSubstring(string s) { unordered_map<char, int> word2Count; int n = s.size(); int left = 0; int ans = 0; for(int i = 0 ; i < n ; i++) { if(word2Count.find(s[i]) == word2Count.end()) { word2Count[s[i]] = 1; } else { word2Count[s[i]] ++; } while(word2Count[s[i]] > 1) { word2Count[s[left]] --; left ++; } ans = max(ans, i - left + 1); } return ans; } }; 應用: 把pointer to list node當key https://leetcode.com/problems/copy-list-with-random-pointer/description/ class Solution { public: Node* copyRandomList(Node* head) { unordered_map<Node*, Node*> old2New; Node* oldCur = head; Node* newHead = nullptr; while(oldCur != nullptr) { Node* curRandom = oldCur->random; Node* curNext = oldCur->next; Node* newCur = nullptr; Node* newRand = nullptr; Node* newNext = nullptr; if(old2New.find(oldCur) == old2New.end()) { newCur = new Node(oldCur->val); old2New[oldCur] = newCur; } if(curRandom && old2New.find(curRandom) == old2New.end() ) { newRand = new Node(curRandom->val); old2New[curRandom] = newRand; } if(curNext && old2New.find(curNext) == old2New.end()) { newNext = new Node(curNext->val); old2New[curNext] = newNext; } newCur = old2New[oldCur]; newRand = old2New[curRandom]; newNext = old2New[curNext]; newCur->next = newNext; newCur->random = newRand; if(!newHead) { newHead = newCur; } oldCur = oldCur->next; } return newHead; } }; 5. set/unordered_set 與map/unordered_map類似, 只是只有key, 沒有value 宣告 unordered_set<int> intSet; unordered_set<char> charSet; unordered_set<string> strSet; Member function intSet.insert(0); if(intSet.find(0) != intSet.end()) { cout <<Key 0 found\n"; }//Will output intSet.erase(0); if(intSet.find(0) == intSet.end()) { cout <<Key 0 not exist\n"; }//Will output 應用: Contain duplicated: 檢查一個array是否有重複數字 https://leetcode.com/problems/contains-duplicate/description/ class Solution { public: bool containsDuplicate(vector<int>& nums) { unordered_set<int> existedNums; for(auto num : nums) { if(existedNums.find(num) != existedNums.end()) { return true; } existedNums.insert(num); } return false; } }; 6. priority_queue Heap, O(logn) insert, delete, O(1) check top element. 宣告 priority_queue<int> MaxPQ; //Max heap 參數: 1. 要存的data type (int) 2. 底層的資料結構 vector<type> 3. comparetor, default: less, 要把它變成min heap要用greater<int> priority_queue<int, vector<int>, greater<int> > MinPQ; //Min Heap 如果要存類似 (value, index) 這樣的pair 可以用pair<int, int>, 並且將value放在pair的第一個 priority_queue<pair<int, int>> MaxPQ; priority_queue<pair<int, int>, vector<pair<int,int>, greater<pair<int,int>> > MinPQ; Member function MaxPQ.push(1); MaxPQ.push(8); MaxPQ.push(5); while(!MaxPQ.empty()) { int curMax = MaxPQ.top(); cout << curMax << " "; MaxPQ.pop(); } //8, 5, 1 應用: TOP-k相關問題 https://leetcode.com/problems/top-k-frequent-elements/ 1. 用一個hash map存所有element出現次數 2. apporoach 1: 用出現次數sort -> nlogn 3. apporoach 2: maintain一個min heap, 只保留前K個最大的. 當目前heap size >= k, 比較下一個number跟目前heap的top(目前最小值), 決定是否把heap的top刪除並加下一個number -> nlogk class Solution { public: vector<int> topKFrequent(vector<int>& nums, int k) { unordered_map<int, int> number2Count; for(int num : nums) { number2Count[num] ++; } vector<int> uniqueNums; for(auto it : number2Count) { uniqueNums.push_back(it.first); } priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>> > PQ; int n = uniqueNums.size(); for(int i = 0 ; i < n; i++) { int curNum = uniqueNums[i]; int curFreq = number2Count[curNum]; if(PQ.size() >= k) { if(PQ.top().first < curFreq) { PQ.pop(); PQ.push({curFreq, curNum}); } } else { PQ.push({curFreq, curNum}); } } vector<int> ret; while(!PQ.empty()) { ret.push_back(PQ.top().second); PQ.pop(); } return ret; } }; 應用: Sliding window maximum https://leetcode.com/problems/sliding-window-maximum/description/ 給一個window size, window一直向右, 求每個時間點的最大值 * maintain一個max heap * 先加入前k個number * 在window滑行中, 檢查目前最大的是否已經不在window內 -> index <= i - k * 加入下一個number * 檢查目前heap最大值 * class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { priority_queue<pair<int,int>> PQ; int n = nums.size(); for(int i = 0 ; i < k ; i++) { PQ.push({nums[i] , i}); } vector<int> ret; ret.push_back(PQ.top().first); for(int i = k ; i < n; i++) { while(!PQ.empty() && PQ.top().second <= (i - k)) { PQ.pop(); } PQ.push({nums[i], i }); ret.push_back(PQ.top().first); } return ret; } }; 應用:最短路徑Dijkstra/ 最小生成樹Prim's algorithm 7. deque Dobule-end queue, 可以從前後push/pop Member function deque<int> dq; dq.push_back(0); //0 dq.push_front(1); //1 0 dq.push_back(2); // 1 0 2 dq.push_front(3); // 3 1 0 2 dq.pop_front(); // 1 0 2 dq.pop_back(); // 1 0 應用: sliding window maximum, 題目敘述與priority_queue提到的相同, 但這次我們加入monotonic stack的做法. https://leetcode.com/problems/sliding-window-maximum/description/ 1 先加入前k個element, 當目前DQ後面有比要加入的element小的, pop back, 這樣DQ的擺放順序就會由大到小, front為目前window最大的 2 每次window滑動時, 檢查最前面的element是否已經不在window內 3 每次加入新element前, pop back掉所有比他小的 4 檢查DQ最前面的element, 就是目前window的最大值 class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { int n = nums.size(); deque<int> DQ; vector<int> ret; for(int i = 0 ; i < k ; i++) { while(!DQ.empty() && nums[DQ.back()] < nums[i] ) { DQ.pop_back(); } DQ.push_back(i); } ret.push_back(nums[DQ.front()]); for(int i = k ; i < n; i++) { while(!DQ.empty() && DQ.front() <= i - k) { DQ.pop_front(); } while(!DQ.empty() && nums[DQ.back()] < nums[i] ) { DQ.pop_back(); } DQ.push_back(i); ret.push_back(nums[DQ.front()]); } return ret; } }; 8. string 宣告 string emptyStr = ""; string someValueStr = "abcdefg"; Member function (usage) string curStr = ""; for(char c = 'a' ; c <= 'd' ; c++) { curStr += c; } //abcd /* s.substr(pos, len) pos: starting index to get substring len: length of substring */ string sub = curStr.substr(0, 2); //ab curStr.push_back('e'); //abcde curStr.pop_back(); //abcd int num1 = stoi("123456"); //stoi: string to integer string num1_str = to_string(num1); //number to string 應用: 字串處理相關問題 9. Iterator Iterator是一種object, 可以先理解為一種比較特別的指標(運算類似) 在C++一些對於container內建的algorithm, return value常常是iterator. vector<int> vec; for(int i = 0 ; i < 5 ; i++) { vec.push_back(i); } //0 1 2 3 4 //Traverse container using iterator for(auto it = vec.begin() ; it != vec.end() ; it ++) { cout << *it << " "; }// 0 1 2 3 4 //Find specific element //If element not existed, vec.end() will be the return value auto it = find(vec.begin(), vec.end(), 2); if(it != vec.end()) { cout << *it << endl; //2 } //Use iterator as member function argument vec.erase(it); //0 1 3 4