diff --git a/Topic1_Arrays/Day2207/Day2207_Tuan/1_TwoSum.txt b/Topic1_Arrays/Day2207/Day2207_Tuan/1_TwoSum.txt new file mode 100644 index 0000000..8eca63b --- /dev/null +++ b/Topic1_Arrays/Day2207/Day2207_Tuan/1_TwoSum.txt @@ -0,0 +1,16 @@ +//Complexity: O(n) + +class Solution { +public: + vector twoSum(vector& nums, int target) { + unordered_map mp; + for(int i = 0; i < nums.size(); i++){ + if(mp.find(target - nums[i]) == mp.end()){ + mp[nums[i]] = i; + }else{ + return {mp[target-nums[i]], i}; + } + } + return {-1, -1}; + } +}; \ No newline at end of file diff --git a/Topic1_Arrays/Day2207/Day2207_Tuan/2_NumberOfIslands.txt b/Topic1_Arrays/Day2207/Day2207_Tuan/2_NumberOfIslands.txt new file mode 100644 index 0000000..5fb0baf --- /dev/null +++ b/Topic1_Arrays/Day2207/Day2207_Tuan/2_NumberOfIslands.txt @@ -0,0 +1,41 @@ +//Complexity: O(n*m) + +class Solution { +public: + int numIslands(vector>& grid) { + queue> q; + int rows = grid.size(); + int cols = grid[0].size(); + int cnt = 0; + vector> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + for(int i = 0; i < rows; i++){ + for(int j = 0; j < cols; j++){ + if(grid[i][j] == '1'){ + pair p; + p.first = i; + p.second = j; + q.push(p); + grid[i][j] = '0'; + ++cnt; + cout << cnt << "\n"; + while(!q.empty()){ + pair peek = q.front(); + q.pop(); + cout << peek.first << " " << peek.second << "\n"; + for(pair dir : directions){ + int neighboor_x = peek.first + dir.first; + int neighboor_y = peek.second + dir.second; + if(neighboor_x >= 0 && neighboor_x < rows && neighboor_y >= 0 + && neighboor_y < cols && grid[neighboor_x][neighboor_y] == '1'){ + pair neighboor = {neighboor_x, neighboor_y}; + q.push(neighboor); + grid[neighboor_x][neighboor_y] = '0'; + } + } + } + } + } + } + return cnt; + } +}; \ No newline at end of file diff --git a/Topic1_Arrays/Day2207/Day2207_Tuan/3_MergeSortedArrays.txt b/Topic1_Arrays/Day2207/Day2207_Tuan/3_MergeSortedArrays.txt new file mode 100644 index 0000000..1d3d1d8 --- /dev/null +++ b/Topic1_Arrays/Day2207/Day2207_Tuan/3_MergeSortedArrays.txt @@ -0,0 +1,18 @@ +//Complexity: O(n+m) + +class Solution { +public: + void merge(vector& nums1, int m, vector& nums2, int n) { + int i = m - 1, j = n - 1, k = n + m - 1; + while(j>=0){ + if(i >= 0 && nums1[i] > nums2[j]){ + nums1[k] = nums1[i]; + i--; k--; + } + else{ + nums1[k] = nums2[j]; + j--; k--; + } + } + } +}; \ No newline at end of file diff --git a/Topic1_Arrays/Day2307/Day2307_Tuan/1_RandomPickWithWeight.txt b/Topic1_Arrays/Day2307/Day2307_Tuan/1_RandomPickWithWeight.txt new file mode 100644 index 0000000..1ac2016 --- /dev/null +++ b/Topic1_Arrays/Day2307/Day2307_Tuan/1_RandomPickWithWeight.txt @@ -0,0 +1,30 @@ +// Time complexity: O(N + log N) +// Using the interval between 2 index of cumulative sum to represent the Selection Possibility +class Solution { +private: + vector cumulativeSum; +public: + Solution(vector& w) { + // Calculate cumulative sum according to weight[i] + int sum = 0; + for(int weight : w){ + sum += weight; + cumulativeSum.push_back(sum); + } + } + // Using binary search to find the possibility of random weight + int pickIndex() { + // +1 at the end of randWeight to include the last element + int randWeight = rand() % cumulativeSum.back() + 1; + int low = 0, high = cumulativeSum.size()-1; + while(low < high){ + int mid = (low + high) / 2; + if(randWeight <= cumulativeSum[mid]){ + high = mid; + } else { + low = mid + 1; + } + } + return low; + } +}; \ No newline at end of file diff --git a/Topic1_Arrays/Day2307/Day2307_Tuan/2_MergeIntervals.txt b/Topic1_Arrays/Day2307/Day2307_Tuan/2_MergeIntervals.txt new file mode 100644 index 0000000..dffcc2c --- /dev/null +++ b/Topic1_Arrays/Day2307/Day2307_Tuan/2_MergeIntervals.txt @@ -0,0 +1,22 @@ +//Complexity: O(n) + +class Solution { +public: + vector> merge(vector>& intervals) { + sort(intervals.begin(), intervals.end()); + + vector> merged; + vector previous = intervals[0]; + for(int i = 1; i < intervals.size(); i++){ + if(previous[1] >= intervals[i][0]){ + previous[1] = max(previous[1], intervals[i][1]); + } else { + merged.push_back(previous); + previous = intervals[i]; + } + } + merged.push_back(previous); + + return merged; + } +}; \ No newline at end of file diff --git a/Topic1_Arrays/Day2307/Day2307_Tuan/3_BestTimeToBuyAndSellStock.txt b/Topic1_Arrays/Day2307/Day2307_Tuan/3_BestTimeToBuyAndSellStock.txt new file mode 100644 index 0000000..e8484b4 --- /dev/null +++ b/Topic1_Arrays/Day2307/Day2307_Tuan/3_BestTimeToBuyAndSellStock.txt @@ -0,0 +1,17 @@ +//Complexity: O(n) + +class Solution { +public: + int maxProfit(vector& prices) { + int length = prices.size(); + int buyPrice = prices[0]; + int profit = 0; + for(int i = 1; i < length; i++){ + if(prices[i] < buyPrice){ + buyPrice = prices[i]; + } + profit = max(profit, prices[i] - buyPrice); + } + return profit; + } +}; \ No newline at end of file diff --git a/Topic1_Arrays/Day2407/Day2407_Tuan/1_GroupAnagrams.txt b/Topic1_Arrays/Day2407/Day2407_Tuan/1_GroupAnagrams.txt new file mode 100644 index 0000000..ae13ff4 --- /dev/null +++ b/Topic1_Arrays/Day2407/Day2407_Tuan/1_GroupAnagrams.txt @@ -0,0 +1,22 @@ +// Time complexity: O(n) +// Space complexity: O(n) - Using map +// Explanation: Because all the anagrams have the same sorted string. +// So, using map to store >. + +class Solution { +public: + vector> groupAnagrams(vector& strs) { + unordered_map> mp; + for(string str : strs){ + string word = str; + sort(word.begin(), word.end()); + mp[word].push_back(str); + } + + vector> ans; + for(auto key : mp){ + ans.push_back(key.second); + } + return ans; + } +}; \ No newline at end of file diff --git a/Topic1_Arrays/Day2407/Day2407_Tuan/3_3Sum.txt b/Topic1_Arrays/Day2407/Day2407_Tuan/3_3Sum.txt new file mode 100644 index 0000000..a1a3607 --- /dev/null +++ b/Topic1_Arrays/Day2407/Day2407_Tuan/3_3Sum.txt @@ -0,0 +1,38 @@ +// Time complexity: O(n^2) +// Space complexity: O(1) +// Explanation: Using 1 pivot i and 2 pointer j and k +// Skip the duplicate elements + +class Solution { + public List> threeSum(int[] nums) { + List> res = new ArrayList<>(); + Arrays.sort(nums); + int len = nums.length; + for(int i = 0; i < len - 2; i++){ + // Skip duplicate elements in nums array to avoid duplicate triplets + if(i > 0 && nums[i] == nums[i-1]) + continue; + + // i is like the pivot + // j/k is left/right ptr of the rest interval + int j = i + 1, k = len - 1; + + while(j < k){ + int sum = nums[i] + nums[j] + nums[k]; + if(sum > 0){ + --k; + }else if(sum < 0){ + ++j; + }else{ + res.add(Arrays.asList(nums[i], nums[j], nums[k])); + ++j; + + // Avoid duplicate nums[j] if nums[j] == nums[j-1] + while(nums[j] == nums[j-1] && j < k) + ++j; + } + } + } + return res; + } +} \ No newline at end of file diff --git a/Topic1_Arrays/Day2507/Day2507_Tuan/3_FindCommonCharacters.txt b/Topic1_Arrays/Day2507/Day2507_Tuan/3_FindCommonCharacters.txt new file mode 100644 index 0000000..97b7cbc --- /dev/null +++ b/Topic1_Arrays/Day2507/Day2507_Tuan/3_FindCommonCharacters.txt @@ -0,0 +1,25 @@ +// Time complexity: O(n) +// Space complexity: O(n) - n: words.length +// Explanation: Using 2d array to store the frequency of each word + +class Solution { + public List commonChars(String[] words) { + int[][] freq = new int[words.length][26]; + for (int i = 0; i < words.length; i++) { + for (char c : words[i].toCharArray()) { + freq[i][c - 'a']++; + } + } + List res = new ArrayList<>(); + for (int i = 'a'; i <= 'z'; i++) { + int min = Integer.MAX_VALUE; + for (int j = 0; j < freq.length; j++) { + min = Math.min(min, freq[j][i-'a']); + } + for (int j = 0; j < min; j++) { + res.add(String.valueOf((char) i)); + } + } + return res; + } +} \ No newline at end of file diff --git a/Topic1_Arrays/Day2607/Day2607_Tuan/3_MaximumSubarray.txt b/Topic1_Arrays/Day2607/Day2607_Tuan/3_MaximumSubarray.txt new file mode 100644 index 0000000..9854c69 --- /dev/null +++ b/Topic1_Arrays/Day2607/Day2607_Tuan/3_MaximumSubarray.txt @@ -0,0 +1,17 @@ +// Time complexity: O(n) +// Space complexity: O(n) +// Explanation: Using array tracking the current sum to compare the previous sum and +// Compare the previous sum and current num + +class Solution { + public int maxSubArray(int[] nums) { + int res = nums[0]; + int[] currSum = new int[nums.length]; + currSum[0] = nums[0]; + for(int i = 1; i < nums.length; i++){ + currSum[i] = Math.max(nums[i], nums[i] + currSum[i-1]); + res = Math.max(currSum[i], res); + } + return res; + } +} \ No newline at end of file diff --git a/Topic1_Arrays/Day2707/2707_Tuan/3_MajorityElement.txt b/Topic1_Arrays/Day2707/2707_Tuan/3_MajorityElement.txt new file mode 100644 index 0000000..a5dde78 --- /dev/null +++ b/Topic1_Arrays/Day2707/2707_Tuan/3_MajorityElement.txt @@ -0,0 +1,17 @@ +// Time complexity: O(n) +// Space complexity: O(n) +// Explanation: Using hash map to store frequency + +class Solution { + public int majorityElement(int[] nums) { + HashMap hm = new HashMap<>(); + int res = 0; + for(int num : nums){ + hm.put(num, hm.getOrDefault(num, 0) + 1); + if(hm.get(num) > nums.length/2){ + res = num; + } + } + return res; + } +} \ No newline at end of file diff --git a/Topic2_LinkedList/LinkedList_HMTuan/1_ReverseLinkedList.txt b/Topic2_LinkedList/LinkedList_HMTuan/1_ReverseLinkedList.txt new file mode 100644 index 0000000..bff06c0 --- /dev/null +++ b/Topic2_LinkedList/LinkedList_HMTuan/1_ReverseLinkedList.txt @@ -0,0 +1,30 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + ListNode(int x, ListNode *next) : val(x), next(next) {} + }; + */ + +// Time Complexity: O(n) +// Space Complexity: O(1) +// Explanation: Using *temp to store the next pointer of head(head->next). +// Because after that, the head->next will point to *rev pointer. +// Expected: rev <- 1 <- 2 <- 3 + +class Solution { +public: + ListNode *reverseList(ListNode *head) { + ListNode *rev = nullptr; + while(head != nullptr){ + ListNode *temp = head->next; + head->next = rev; + rev = head; + head = temp; + } + return rev; + } +}; \ No newline at end of file diff --git a/Topic2_LinkedList/LinkedList_HMTuan/2_Merge2SortedLists.txt b/Topic2_LinkedList/LinkedList_HMTuan/2_Merge2SortedLists.txt new file mode 100644 index 0000000..15353ba --- /dev/null +++ b/Topic2_LinkedList/LinkedList_HMTuan/2_Merge2SortedLists.txt @@ -0,0 +1,47 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +// Time complexity: O(n+m)- n: size of list1, m: size of list2 +// Space complexity: O(1) - The algorithm merges the lists by rearranging the 'next' pointers of node. +// It does not create a new nodes -> no use additional data structure for storing nodes. +// Explaination: Use a dummy node as starting point. +// Then traverse 2 linked list and pick the smallest element to push back at each iterate. + +class Solution { +public: + ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) { + // Dummy node to act as the starting point + ListNode dummy; + ListNode *tail = &dummy; + + // Traverse both lists + while (list1 != nullptr && list2 != nullptr) { + if (list1->val <= list2->val) { + tail->next = list1; + list1 = list1->next; + } else { + tail->next = list2; + list2 = list2->next; + } + tail = tail->next; + } + + // Attach the remaining nodes + if (list1 != nullptr) { + tail->next = list1; + } else { + tail->next = list2; + } + + // Return the merged list, starting from dummy.next + return dummy.next; + } +}; \ No newline at end of file diff --git a/Topic2_LinkedList/LinkedList_HMTuan/3_ReorderList.txt b/Topic2_LinkedList/LinkedList_HMTuan/3_ReorderList.txt new file mode 100644 index 0000000..e196ff0 --- /dev/null +++ b/Topic2_LinkedList/LinkedList_HMTuan/3_ReorderList.txt @@ -0,0 +1,44 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ +// Time Complexity: O(n + n/2) +// Space Complexity: O(n) - using deque to store all the nodes of linked list. +// Explanation: Using double-ended queue (deque) to store and select front, back node. + +class Solution { +public: + void reorderList(ListNode* head) { + // Store list nodes in double-ended queue + ListNode* temp = head; + deque dq; + while(temp != nullptr) { + dq.push_back(temp); + temp = temp->next; + } + + // Reordering the list + while(!dq.empty()) { + // Set the next node to the front node of deque + head->next = dq.front(); + dq.pop_front(); + head = head->next; + + // Set the next node to the back node of deque + if (!dq.empty()) { + head->next = dq.back(); + dq.pop_back(); + head = head->next; + } + } + + //Set the next of last node to nullptr + head->next = nullptr; + } +}; \ No newline at end of file diff --git a/Topic2_LinkedList/LinkedList_HMTuan/4_RemoveNthNodeFromTheEndOfList.txt b/Topic2_LinkedList/LinkedList_HMTuan/4_RemoveNthNodeFromTheEndOfList.txt new file mode 100644 index 0000000..3f67a52 --- /dev/null +++ b/Topic2_LinkedList/LinkedList_HMTuan/4_RemoveNthNodeFromTheEndOfList.txt @@ -0,0 +1,44 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +// Time complexity: O(n) +// Space complexity: O(n) - using stack to store n nodes +// Explaination: Pushing all nodes into stack and pop of n times to find the one to be removed. + +class Solution { +public: + ListNode* removeNthFromEnd(ListNode* head, int n) { + stack nodeStack; + ListNode* current = head; + while(current != nullptr){ + nodeStack.push(current); + current = current->next; + } + + // Pop n nodes off the stack + while(n-- && !nodeStack.empty()) { + nodeStack.pop(); + } + + // *Edge case: the 1st node to be removed + if (nodeStack.empty()) { + ListNode* newHead = head->next; + return newHead; + } + + // Assign the next of the previous node to the next of node to remove + ListNode* prev = nodeStack.top(); + ListNode* nodeToRemove = prev->next; + prev->next = nodeToRemove->next; + + return head; + } +}; \ No newline at end of file diff --git a/Topic2_LinkedList/LinkedList_HMTuan/5_CopyListWithRandomPointer.cpp b/Topic2_LinkedList/LinkedList_HMTuan/5_CopyListWithRandomPointer.cpp new file mode 100644 index 0000000..08526db --- /dev/null +++ b/Topic2_LinkedList/LinkedList_HMTuan/5_CopyListWithRandomPointer.cpp @@ -0,0 +1,44 @@ +/* +// Definition for a Node. +class Node { +public: + int val; + Node* next; + Node* random; + + Node(int _val) { + val = _val; + next = NULL; + random = NULL; + } +}; +*/ + +// Time complexity: O(n) +// Space complexity: O(n) - use unordered_map to store pair +// Explanation: Traverse twice the list: 1st, create empty corresponding copied node. +// 2nd, assign next, random pointer to copied node. + +class Solution { +public: + Node* copyRandomList(Node* head) { + unordered_map map; + Node* curr = head; + // Store corresponding copied node to original node in hash map + while(curr){ + map[curr] = new Node(curr->val); + curr = curr->next; + } + + curr = head; + // Mapping the next, random pointer of original node to correspoding one; + while(curr){ + map[curr]->next = map[curr->next]; + map[curr]->random = map[curr->random]; + + curr = curr->next; + } + + return map[head]; + } +}; \ No newline at end of file diff --git a/Topic2_LinkedList/LinkedList_HMTuan/6_Add2Numbers.txt b/Topic2_LinkedList/LinkedList_HMTuan/6_Add2Numbers.txt new file mode 100644 index 0000000..e9d1003 --- /dev/null +++ b/Topic2_LinkedList/LinkedList_HMTuan/6_Add2Numbers.txt @@ -0,0 +1,46 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +// Time complexity: O(max(m, n)) m-size of l1, n-size of l2. Because of iterating till end of both ListNode. +// Space complexity: O(max(m, n)) +// Explanation: Add 2 corresponding node and handle carry like normal. +// *Remember* to handle the last carry + +class Solution { +public: + ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { + int carry = 0; + ListNode* dummy = new ListNode(); + ListNode* sumList = dummy; + + // And 2 val from 2 correspoding nodes + while(l1 != nullptr || l2 != nullptr ){ + // Check if access to null pointer + int l1Val = (l1 != nullptr) ? l1->val : 0; + int l2Val = (l2 != nullptr) ? l2->val : 0; + + int sum = l1Val + l2Val + carry; + carry = sum / 10; + ListNode* newNode = new ListNode(sum%10); + + sumList->next = newNode; + sumList = sumList->next; + if(l1 != nullptr) l1 = l1->next; + if(l2 != nullptr) l2 = l2->next; + } + + // Handle if there is exist the last carry + if(carry > 0){ + sumList->next = new ListNode(carry); + } + return dummy->next; + } +}; \ No newline at end of file diff --git a/Topic2_LinkedList/LinkedList_HMTuan/7_LinkedListCycle.txt b/Topic2_LinkedList/LinkedList_HMTuan/7_LinkedListCycle.txt new file mode 100644 index 0000000..eb50583 --- /dev/null +++ b/Topic2_LinkedList/LinkedList_HMTuan/7_LinkedListCycle.txt @@ -0,0 +1,33 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode(int x) : val(x), next(NULL) {} + * }; + */ + +// Time complexity: O(n) +// Space complexity: O(1) +// Explanation: Using 2 pointer fast and slow (Tortoise and Hare algorithm). +// The fast pointer moves twice as fast as the slow pointer. +// If there is cycle they will eventually meet. + +class Solution { +public: + bool hasCycle(ListNode *head) { + // Tortoise and Hare algorithm + ListNode* fast = head; + ListNode* slow = head; + while(fast != nullptr && fast->next != nullptr){ + slow = slow->next; + fast = fast->next->next; + // No matter how far between fast and slow pointer. + // If there is cycle they will eventually meet + if(slow == fast){ + return true; + } + } + return false; + } +}; \ No newline at end of file diff --git a/Topic3_StackQueue/StackQueue_HMTuan/1_ValidPranthese.cpp b/Topic3_StackQueue/StackQueue_HMTuan/1_ValidPranthese.cpp new file mode 100644 index 0000000..1c27b6c --- /dev/null +++ b/Topic3_StackQueue/StackQueue_HMTuan/1_ValidPranthese.cpp @@ -0,0 +1,29 @@ +// Time complexity: O(n) +// Space complexity: O(n) - using stack to store char +// Explanation: Iterate through given string. +// Push open bracket and pop the corresponding closing one. + +class Solution { +public: + bool isValid(string s) { + stack st; + for(char c : s){ + if(st.empty()){ + st.push(c); + } + else{ + if(st.top() == '(' && c == ')'){ + st.pop(); + }else if(st.top() == '{' && c == '}'){ + st.pop(); + }else if(st.top() == '[' && c == ']'){ + st.pop(); + }else{ + st.push(c); + } + } + } + if(st.empty()) return true; + return false; + } +}; \ No newline at end of file diff --git a/Topic3_StackQueue/StackQueue_HMTuan/2_MinStack.cpp b/Topic3_StackQueue/StackQueue_HMTuan/2_MinStack.cpp new file mode 100644 index 0000000..72d1210 --- /dev/null +++ b/Topic3_StackQueue/StackQueue_HMTuan/2_MinStack.cpp @@ -0,0 +1,50 @@ +// Time complexity: O(1) for each operation (push, pop, getMin) +// Space complexity: O(n) - using Stack to store pairs +// Explanation: The code explains itself =)) + +class MinStack { +public: + stack> st; + MinStack() { + } + + void push(int val) { + int currMin; + if(st.empty()){ + currMin = val; + }else{ + // if stack not empty: Compare current min(st.top().second) & pushing value to update + currMin = min(st.top().second, val); + } + st.push({val, currMin}); + } + + void pop() { + if(!st.empty()){ + st.pop(); + } + } + + int top() { + if(!st.empty()) + return st.top().first; + else + return -1; + } + + int getMin() { + if(!st.empty()) + return st.top().second; + else + return -1; + } +}; + +/** + * Your MinStack object will be instantiated and called as such: + * MinStack* obj = new MinStack(); + * obj->push(val); + * obj->pop(); + * int param_3 = obj->top(); + * int param_4 = obj->getMin(); + */ \ No newline at end of file diff --git a/Topic3_StackQueue/StackQueue_HMTuan/3_EvaluateReversePolishNotation.cpp b/Topic3_StackQueue/StackQueue_HMTuan/3_EvaluateReversePolishNotation.cpp new file mode 100644 index 0000000..c76ba30 --- /dev/null +++ b/Topic3_StackQueue/StackQueue_HMTuan/3_EvaluateReversePolishNotation.cpp @@ -0,0 +1,40 @@ +// Time complexity: O(n) +// Space complexity: O(1) +// Explanation: Using stack to store operand. +// If meet operator pop 2 latest operands out of stack for calculation. +// Then, push the result of calculation to the stack. + +class Solution { +public: + bool isOperator(string tok){ + return tok == "+" || tok == "-" || tok == "*" || tok == "/"; + } + + int calc(int operand1, int operand2, string tok){ + if(tok == "+") + return operand1 + operand2; + else if(tok == "-") + return operand1 - operand2; + else if(tok == "*") + return operand1 * operand2; + else if(tok == "/") + return operand1 / operand2; + + return 0; + } + int evalRPN(vector& tokens) { + stack st; + for(string tok : tokens){ + if(isOperator(tok)){ + int operand2 = st.top(); + st.pop(); + int operand1 = st.top(); + st.pop(); + st.push(calc(operand1, operand2, tok)); + }else{ + st.push(stoi(tok)); + } + } + return st.top(); + } +}; \ No newline at end of file diff --git a/Topic3_StackQueue/StackQueue_HMTuan/4_DailyTemperatures.cpp b/Topic3_StackQueue/StackQueue_HMTuan/4_DailyTemperatures.cpp new file mode 100644 index 0000000..10f82bc --- /dev/null +++ b/Topic3_StackQueue/StackQueue_HMTuan/4_DailyTemperatures.cpp @@ -0,0 +1,26 @@ +// Time complexity: O(n) +// Space complexity: O(n) - Using stack +// Explanation: Using stack to store previous temperature that lower. +// If meet temperature higher pop until the top of stack < current temperature. +// Else, push the temperature in stack. + +class Solution { +public: + vector dailyTemperatures(vector& temperatures) { + int len = temperatures.size(); + vector ans(len); + stack st; + for(int i = 0; i < len; i++){ + while(!st.empty() && temperatures[st.top()] < temperatures[i]){ + ans[st.top()] = i - st.top(); + st.pop(); + } + st.push(i); + } + while(!st.empty()){ + ans[st.top()] = 0; + st.pop(); + } + return ans; + } +}; \ No newline at end of file diff --git a/Topic3_StackQueue/StackQueue_HMTuan/5_RevealCardInIncreasingOrder.cpp b/Topic3_StackQueue/StackQueue_HMTuan/5_RevealCardInIncreasingOrder.cpp new file mode 100644 index 0000000..e165ef6 --- /dev/null +++ b/Topic3_StackQueue/StackQueue_HMTuan/5_RevealCardInIncreasingOrder.cpp @@ -0,0 +1,29 @@ +// Time complexity: O(n) +// Space complexity: O(n) - Using deque, vector +// Explanation: Sort first to get the wanted result. +// Then, do all steps backwardly + +class Solution { +public: + vector deckRevealedIncreasing(vector& deck) { + sort(deck.begin(), deck.end()); + + int len = deck.size(); + deque dq; + for(int i = len-1; i >= 0; i--){ + if(!dq.empty()){ + int back = dq.back(); + dq.pop_back(); + dq.push_front(back); + } + dq.push_front(i); + } + + vector ans; + while(!dq.empty()){ + ans.push_back(deck[dq.front()]); + dq.pop_front(); + } + return ans; + } +}; \ No newline at end of file diff --git a/Topic3_StackQueue/StackQueue_HMTuan/7_FindTheWinnerOfTheCircularGame.cpp b/Topic3_StackQueue/StackQueue_HMTuan/7_FindTheWinnerOfTheCircularGame.cpp new file mode 100644 index 0000000..788bfc6 --- /dev/null +++ b/Topic3_StackQueue/StackQueue_HMTuan/7_FindTheWinnerOfTheCircularGame.cpp @@ -0,0 +1,32 @@ +// Time complexity: O(n*k) - multiple k because we have to push k element after pop of each iterate +// Space complexity: O(n) - Using deque +// Explanation: Everything is relative. +// Instead of the arrow moves clockwise, the circle is the one that actually move +// Ex: n = 5, k = 2 +// Step 0: 1 2 3 4 5 +// Step 1: 2 3 4 5 1 (i = 1, pop the front and push it in the back of queue) +// Step 2: 3 4 5 1 (pop the front) +// Step 3: 4 5 1 3 (i = 1, pop the front and push it in the back of queue) +// Step 4: 5 1 3 (pop the front) +// Step 5: 1 3 5 +// Step 6: 3 5 +// ..... + +class Solution { +public: + int findTheWinner(int n, int k) { + deque dq; + for(int i = 1; i <= n; i++){ + dq.push_back(i); + } + while(dq.size() > 1){ + for(int i = 1; i < k; i++){ + int front = dq.front(); + dq.pop_front(); + dq.push_back(front); + } + dq.pop_front(); + } + return dq.front(); + } +}; \ No newline at end of file diff --git a/Topic3_StackQueue/StackQueue_HMTuan/8_LongestContinuousSubarrayWithAbsoluteDiffLessThanOrEqualToLimit.cpp b/Topic3_StackQueue/StackQueue_HMTuan/8_LongestContinuousSubarrayWithAbsoluteDiffLessThanOrEqualToLimit.cpp new file mode 100644 index 0000000..435e90c --- /dev/null +++ b/Topic3_StackQueue/StackQueue_HMTuan/8_LongestContinuousSubarrayWithAbsoluteDiffLessThanOrEqualToLimit.cpp @@ -0,0 +1,46 @@ +// Time complexity: O(n) +// Space complexity: O(n) - Using 2 deque +// Explanation: The code explains itself =))) + +class Solution { +public: + int longestSubarray(vector& nums, int limit) { + // Using 2 deque to store current max/min elements of window in order + // to help track max/min element of current window + // maxDq/minDq is in descending/ascending order + deque maxDq; + deque minDq; + + // Using sliding window technique: + // "Expand (++right)" the window if not exceed the given limit + // "Shrink (++left)" the window if exceed the given limit + int ans = 0; + int left = 0; + for(int right = 0; right < nums.size(); right++){ + int num = nums[right]; + while(!maxDq.empty() && num > maxDq.back()){ + maxDq.pop_back(); + } + + while(!minDq.empty() && num < minDq.back()){ + minDq.pop_back(); + } + + maxDq.push_back(num); + minDq.push_back(num); + + // Check if the max, min of current window > limit -> ++left + while(maxDq.front() - minDq.front() > limit){ + if (maxDq.front() == nums[left]) { + maxDq.pop_front(); + } + if (minDq.front() == nums[left]) { + minDq.pop_front(); + } + ++left; + } + ans = max(ans, right - left + 1); + } + return ans; + } +}; \ No newline at end of file diff --git a/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b1_GenerateParantheses.cpp b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b1_GenerateParantheses.cpp new file mode 100644 index 0000000..51ff808 --- /dev/null +++ b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b1_GenerateParantheses.cpp @@ -0,0 +1,29 @@ +// Time complexity: O(2^2n) - There are 2n position, each position have 2 possible options '(', ')' +// Space complexity: O(n) +// Explanation: Using a recursive DFS approach to explore all possible valid combinations of parentheses. +// We prioritize adding an open parenthesis whenever possible. +// And, we add a closing parenthesis only when it won't result in an invalid sequence. + +class Solution { +public: + vector ans; + int pairN; + void dfs(int openP, int closeP, string temp){ + if(openP == pairN && closeP == pairN){ + ans.push_back(temp); + return; + } + + if(openP < pairN) + dfs(openP + 1, closeP, temp + "("); + + if(openP > closeP) + dfs(openP, closeP + 1, temp + ")"); + } + + vector generateParenthesis(int n) { + pairN = n; + dfs(0, 0, ""); + return ans; + } +}; \ No newline at end of file diff --git a/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b2_LetterCombinationsOfAPhoneNumber.cpp b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b2_LetterCombinationsOfAPhoneNumber.cpp new file mode 100644 index 0000000..2304338 --- /dev/null +++ b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b2_LetterCombinationsOfAPhoneNumber.cpp @@ -0,0 +1,33 @@ +// Time complexity: O(n^len) n-number of digits per letters(3), len-length of string digits +// Space complexity: O(len * n^len) - recursion stack +// Explanation: Recursion call until the end of string digits +// Each recursion add 1 of the corresponding letter + +class Solution { +public: + int len; + vector ans; + string mapping[10] = {"", "", "abc", "def", "ghi", + "jkl", "mno", "pqrs", "tuv", "wxyz"}; + + void Try(int i, string digits, string combination){ + if(i == len){ + ans.push_back(combination); + return; + } + int key = digits[i] - '0'; + string value = mapping[key]; + for(char c : value){ + Try(i + 1, digits, combination + c); + } + } + + vector letterCombinations(string digits) { + len = digits.size(); + if(len == 0) + return ans; + + Try(0, digits, ""); + return ans; + } +}; \ No newline at end of file diff --git a/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b3_PathWithMaximumGold.cpp b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b3_PathWithMaximumGold.cpp new file mode 100644 index 0000000..a52a234 --- /dev/null +++ b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/b3_PathWithMaximumGold.cpp @@ -0,0 +1,47 @@ +// Time complexity: O() +// Space complexity: O() +// Explanation: Using dfsBacktrack to traverse all appropriate cells +// Set grid[i][j] = 0 if it is visited. +// Backtrack grid[i][j] = prev (previous state before being visited) + +class Solution { +public: + int rows, cols, maxGold; + const pair DIRECTIONS[4] = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; + + void dfsBacktrack(vector> &grid, int i, int j, int currGold){ + // Base case: the cell is not appropriate or has no gold + if(i < 0 || i >= rows || j < 0 || j >= cols || grid[i][j] == 0){ + return; + } + + currGold += grid[i][j]; + maxGold = max(maxGold, currGold); + + // If grid[i][j] is visited, set it to 0 + int prev = grid[i][j]; + grid[i][j] = 0; + + for(auto dir : DIRECTIONS){ + dfsBacktrack(grid, i + dir.first, j + dir.second, currGold); + } + + // Backtrack to the previous state before being visited + grid[i][j] = prev; + } + + int getMaximumGold(vector>& grid) { + rows = grid.size(); + cols = grid[0].size(); + maxGold = 0; + + for(int i = 0; i < rows; i++){ + for(int j = 0; j < cols; j++){ + if(grid[i][j] != 0){ + dfsBacktrack(grid, i, j, 0); + } + } + } + return maxGold; + } +}; \ No newline at end of file diff --git a/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r1_DecodeString.cpp b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r1_DecodeString.cpp new file mode 100644 index 0000000..e165d8c --- /dev/null +++ b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r1_DecodeString.cpp @@ -0,0 +1,51 @@ +// Time complexity: O(n) +// Space complexity: O(n) +// Explanation: The codes explain itself =)) + +class Solution { +public: + string decodeString(string s) { + stack st; + for(char c : s){ + if(c != ']'){ + st.push(c); + continue; + } + + // Extract the encoded string + string encodedStr = ""; + while(!st.empty() && st.top() != '[') { + encodedStr = st.top() + encodedStr; + st.pop(); + } + // Pop '[' + st.pop(); + + // Get the multiplier (number before '[') + string multiplierStr = ""; + while(!st.empty() && isdigit(st.top())) { + multiplierStr = st.top() + multiplierStr; + st.pop(); + } + + // Repeat the encoded string and push back to the stack + int multiplier = stoi(multiplierStr); + string repeatedStr = ""; + for(int i = 0; i < multiplier; ++i) { + repeatedStr += encodedStr; + } + + for(char c : repeatedStr) { + st.push(c); + } + } + + string result = ""; + while(!st.empty()) { + result = st.top() + result; + st.pop(); + } + + return result; + } +}; \ No newline at end of file diff --git a/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r2_Pow(x, n).cpp b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r2_Pow(x, n).cpp new file mode 100644 index 0000000..5add63d --- /dev/null +++ b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r2_Pow(x, n).cpp @@ -0,0 +1,28 @@ +// Time complexity: O(log n) +// Space complexity: O(log n) - use recursion -> maintain call stack +// Explanation: Using a divide-and-conquer approach with recursion to efficiently calculate // the power by halving the exponent at each step. + +class Solution { +public: + double calc(double x, int n){ + if(n == 0){ + return 1; + } + double half = calc(x, n/2); + if(n % 2 == 0){ + return half * half; + }else{ + return half * half * x; + } + } + + double myPow(double x, int n) { + if(n == 0){ + return 1.0; + }else if(n > 0){ + return calc(x, n); + }else{ + return 1 / calc(x, n); + } + } +}; \ No newline at end of file diff --git a/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r3_FindTheWinnerOfTheCircularGame.cpp b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r3_FindTheWinnerOfTheCircularGame.cpp new file mode 100644 index 0000000..3fd1405 --- /dev/null +++ b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r3_FindTheWinnerOfTheCircularGame.cpp @@ -0,0 +1,24 @@ +// Time complexity: O(n*k) +// Space complexity: O(n) +// Explanation: Instead of moving a pointer, moving the circular by using deque +// Pop k-1 people and push them back of deque +// Eliminate the k th person. + +class Solution { +public: + int findTheWinner(int n, int k) { + deque dq; + for(int i = 1; i <= n; i++){ + dq.push_back(i); + } + while(dq.size() > 1){ + for(int i = 1; i < k; i++){ + int front = dq.front(); + dq.pop_front(); + dq.push_back(front); + } + dq.pop_front(); + } + return dq.front(); + } +}; \ No newline at end of file diff --git a/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r5_IntegerToEnglishWords.cpp b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r5_IntegerToEnglishWords.cpp new file mode 100644 index 0000000..408d402 --- /dev/null +++ b/Topic4_RecursionBacktrack/RecursionBacktrack_HMT/r5_IntegerToEnglishWords.cpp @@ -0,0 +1,37 @@ +// Time complexity: O(n) +// Space complexity: O(1) +// Explanation: Using recursion division to break down the number into smaller parts +// e.g: num = 1234. +// The code processes the thousand first, then the hundred, tens, and ones + +class Solution { +public: + vector numbers = { + 1000000000, 1000000, 1000, 100, 90, 80, 70, 60, 50, 40, 30, 20, + 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 + }; //31 numbers + + vector words = { + "Billion", "Million", "Thousand", "Hundred", "Ninety", "Eighty", + "Seventy", "Sixty", "Fifty", "Forty", "Thirty", "Twenty", + "Nineteen", "Eighteen", "Seventeen", "Sixteen", "Fifteen", "Fourteen", + "Thirteen", "Twelve", "Eleven", "Ten", "Nine", "Eight", "Seven", + "Six", "Five", "Four", "Three", "Two", "One" + }; + + string numberToWords(int num) { + string result; + for(int i = 0; i < 31; i++){ + if(num >= numbers[i]){ + if(num >= 100) result += numberToWords(num / numbers[i]) + " " + words[i] + " "; + else result += words[i] + " "; + + num %= numbers[i]; + + if (num == 0) break; + } + } + // result.subtr to eliminate the " " at the end of string + return result.empty() ? "Zero" : result.substr(0, result.size() - 1); + } +}; \ No newline at end of file diff --git a/Topic5_BinarySearch/BinarySearch-HMT/1_BinarySearch.cpp b/Topic5_BinarySearch/BinarySearch-HMT/1_BinarySearch.cpp new file mode 100644 index 0000000..0c941e5 --- /dev/null +++ b/Topic5_BinarySearch/BinarySearch-HMT/1_BinarySearch.cpp @@ -0,0 +1,21 @@ +// Time complexity: O(log n) +// Space complexity: O(1) +// Explanation: The code explains itself =)) + +class Solution { +public: + int search(vector& nums, int target) { + int left = 0, right = nums.size() - 1; + while(left <= right){ + int mid = (left + right) / 2; + if(target == nums[mid]){ + return mid; + }else if(target < nums[mid]){ + right = mid - 1; + }else{ + left = mid + 1; + } + } + return -1; + } +}; \ No newline at end of file diff --git a/Topic5_BinarySearch/BinarySearch-HMT/2_SearchA2DMatrix.cpp b/Topic5_BinarySearch/BinarySearch-HMT/2_SearchA2DMatrix.cpp new file mode 100644 index 0000000..0e37453 --- /dev/null +++ b/Topic5_BinarySearch/BinarySearch-HMT/2_SearchA2DMatrix.cpp @@ -0,0 +1,30 @@ +// Time complexity: O(log n * m) - n is row, m is column +// Space complexity: O(1) +// Explanation: Convert 2d matrix -> 1d array + +class Solution { +public: + bool searchMatrix(vector>& matrix, int target) { + int rows = matrix.size(); + int cols = matrix[0].size(); + int matrixSize = rows * cols; + int left = 0, right = matrixSize - 1; // start from 0, so need to -1 + + // Bisearch + while(left <= right){ + // convert from index of 1d array -> 2d matrix + int mid = (left + right) / 2; + int midRow = mid / cols; + int midCol = mid % cols; + + if(matrix[midRow][midCol] == target){ + return true; + }else if(matrix[midRow][midCol] > target){ + right = mid - 1; + }else{ + left = mid + 1; + } + } + return false; + } +}; \ No newline at end of file diff --git a/Topic5_BinarySearch/BinarySearch-HMT/3_KokoEatingBananas.cpp b/Topic5_BinarySearch/BinarySearch-HMT/3_KokoEatingBananas.cpp new file mode 100644 index 0000000..9bfd7c6 --- /dev/null +++ b/Topic5_BinarySearch/BinarySearch-HMT/3_KokoEatingBananas.cpp @@ -0,0 +1,36 @@ +// Time complexity: O(log(maxPile * lenPile) +// Space complexity: O(1) +// Explanation: minSpeed is in [1, maxPile]. Using Bi-Search to find the minimum speed. + +class Solution { +public: + int calcHour(vector piles, int speed){ + int hour = 0; + // round up to finish leftover + for(int pile : piles){ + hour += (pile + speed - 1) / speed; + } + return hour; + } + + int minEatingSpeed(vector& piles, int h) { + int maxPile; + for(int pile : piles){ + maxPile = max(maxPile, pile); + } + + int l = 1, r = maxPile, res = 0; + while(l <= r){ + int m = l + (r - l) / 2; + int needHour = calcHour(piles, m); + if (needHour <= h) { + // If the hours needed is within the limit, try to find a smaller speed + r = m - 1; + } else { + // If the hours needed exceed the limit, increase the speed + l = m + 1; + } + } + return l; + } +}; \ No newline at end of file diff --git a/Topic5_BinarySearch/BinarySearch-HMT/4_SearchInRotatedSortedArray.cpp b/Topic5_BinarySearch/BinarySearch-HMT/4_SearchInRotatedSortedArray.cpp new file mode 100644 index 0000000..1ff886d --- /dev/null +++ b/Topic5_BinarySearch/BinarySearch-HMT/4_SearchInRotatedSortedArray.cpp @@ -0,0 +1,22 @@ +// Time complexity: O(log n) +// Space complexity: O(1) +// Explanation: Custom binary search +// Using min_ele to handle some special case of rotated sorted array + +class Solution { +public: + int findMin(vector& nums) { + int left = 0, right = nums.size() - 1; + int min_ele = nums[0]; + while(left <= right){ + int mid = (left + right) / 2; + if(nums[mid] <= nums[right]){ + min_ele = min(min_ele, nums[mid]); + right = mid - 1; + }else{ + left = mid + 1; + } + } + return min_ele; + } +}; \ No newline at end of file diff --git a/Topic5_BinarySearch/BinarySearch-HMT/5_SearchInRotatedArray.cpp b/Topic5_BinarySearch/BinarySearch-HMT/5_SearchInRotatedArray.cpp new file mode 100644 index 0000000..ce3bd5e --- /dev/null +++ b/Topic5_BinarySearch/BinarySearch-HMT/5_SearchInRotatedArray.cpp @@ -0,0 +1,34 @@ +// Time complexity: O(logn) +// Space complexity: O(1) +// Explanation: Using custom binary search to detect which sorted side the mid pointer is +// e.g: 3 4 5 1 2 -> The rotated array can be divided into 2 sorted array: +// [3 4 5] [1 2] -> Check the condition to detect which side. +// target = 1; +// mid = 2 , nums[mid] (5) >= nums[left] (3) => mid in the left side +// Then, check the target is in left side, or right side + +class Solution { +public: + int search(vector& nums, int target) { + int left = 0, right = nums.size() - 1; + while(left <= right){ + int mid = (left + right) / 2; + if(nums[mid] == target){ + return mid; + }else if(nums[mid] >= nums[left]){ + // Left sorted side + if (nums[left] <= target && target <= nums[mid]) + right = mid - 1; + else + left = mid + 1; + }else{ + // Right sorted side + if(nums[mid] <= target && target <= nums[right]) + left = mid + 1; + else + right = mid - 1; + } + } + return -1; + } +}; \ No newline at end of file diff --git a/Topic6_Sorting/Sorting-HMT/1_PancakeSorting.cpp b/Topic6_Sorting/Sorting-HMT/1_PancakeSorting.cpp new file mode 100644 index 0000000..dbac00e --- /dev/null +++ b/Topic6_Sorting/Sorting-HMT/1_PancakeSorting.cpp @@ -0,0 +1,27 @@ +// Time complexity: O(n^2) +// Space complexity: O(1) +// Explanation: Idea is same as Subble Sort. +// After each sorting step (i.e bubble the biggest element on the right), fix the end of array and handle the rest. + +class Solution { +public: + vector pancakeSort(vector& arr) { + int n = arr.size(); + vectorans; + for(int i = n; i >= 1; i--){ + for(int j = 0; j < n - 1; j++){ + // If the left ptr is equal to right ptr (i.e ), we move the left ptr to the right ptr and fix the tail + if(arr[j] == i){ + // Reverse to move left ptr to the head of arr + reverse(arr.begin(), arr.begin() + j + 1); + ans.push_back(j + 1); + + // Reverse to move head of arr to the tail of arr + reverse(arr.begin(), arr.begin() + i); + ans.push_back(i); + } + } + } + return ans; + } +}; \ No newline at end of file diff --git a/Topic6_Sorting/Sorting-HMT/3_SortAnArray.cpp b/Topic6_Sorting/Sorting-HMT/3_SortAnArray.cpp new file mode 100644 index 0000000..010cdf0 --- /dev/null +++ b/Topic6_Sorting/Sorting-HMT/3_SortAnArray.cpp @@ -0,0 +1,46 @@ +// Time complexity: O(log n) +// Space complexity: O(n) - 2 additional vector left, right +// Explanation: Using Merge Sort + +class Solution { +public: + void merge(vector &nums, int begin, int mid, int end){ + vector left(nums.begin() + begin, nums.begin() + mid + 1); + vector right(nums.begin() + mid + 1, nums.begin() + end + 1); + int i = 0, j = 0; + while(i < left.size() && j < right.size()){ + if(left[i] <= right[j]){ + nums[begin] = left[i]; + ++i; + }else{ + nums[begin] = right[j]; + ++j; + } + ++begin; + } + while(i < left.size()){ + nums[begin] = left[i]; + ++i; + ++begin; + } + while(j < right.size()){ + nums[begin] = right[j]; + ++j; + ++begin; + } + } + + void mergeSort(vector &nums, int begin, int end){ + if(begin >= end) + return; + int mid = (begin + end) / 2; + mergeSort(nums, begin, mid); + mergeSort(nums, mid+1, end); + merge(nums, begin, mid, end); + } + + vector sortArray(vector& nums) { + mergeSort(nums, 0, nums.size() - 1); + return nums; + } +}; \ No newline at end of file diff --git a/Topic6_Sorting/Sorting-HMT/4_SortColors.cpp b/Topic6_Sorting/Sorting-HMT/4_SortColors.cpp new file mode 100644 index 0000000..7a86744 --- /dev/null +++ b/Topic6_Sorting/Sorting-HMT/4_SortColors.cpp @@ -0,0 +1,25 @@ +// Time complexity: O(n) +// Space complexity: O(1) +// Explanation: Using 3 pointer red, white blue + +class Solution { +public: + void sortColors(vector& nums) { + // 0 - red + // 1 - white + // 2 - blue + int r = 0, w = 0, b = nums.size()-1; + while(w <= b){ + if(nums[w] == 0){ + swap(nums[w], nums[r]); + ++r; + ++w; + }else if(nums[w] == 1){ + ++w; + }else{ + swap(nums[w], nums[b]); + --b; + } + } + } +}; \ No newline at end of file diff --git a/Topic6_Sorting/Sorting-HMT/5_LargestNumber.cpp b/Topic6_Sorting/Sorting-HMT/5_LargestNumber.cpp new file mode 100644 index 0000000..df7b9d3 --- /dev/null +++ b/Topic6_Sorting/Sorting-HMT/5_LargestNumber.cpp @@ -0,0 +1,22 @@ +// Time complexity: O(n*logn) - sort() function +// Space complexity: O(1) - 2 additional vector left, right +// Explanation: Custom sort to determine which combination of a and b will result in the largest number when concatenated + +class Solution { +public: + static bool cmp(int a, int b){ + return to_string(a) + to_string(b) > to_string(b) + to_string(a); + } + + string largestNumber(vector& nums) { + sort(nums.begin(), nums.end(), cmp); + string res; + for(int num : nums){ + res += to_string(num); + } + + if(res[0] == '0') + return "0"; + return res; + } +}; \ No newline at end of file diff --git a/Topic6_Sorting/Sorting-HMT/8_SortCharatersByFrequency.cpp b/Topic6_Sorting/Sorting-HMT/8_SortCharatersByFrequency.cpp new file mode 100644 index 0000000..f0e72ac --- /dev/null +++ b/Topic6_Sorting/Sorting-HMT/8_SortCharatersByFrequency.cpp @@ -0,0 +1,29 @@ +// Time complexity: O(n) +// Space complexity: O(n) - frequency vector +// Explanation: Using map to count frequency. +// Then, copy map into vector and sort vector. + +class Solution { +public: + static bool cmp(pair& a, pair& b){ + return a.second > b.second; + } + + string frequencySort(string s) { + map freqMp; + for(char c : s){ + freqMp[c]++; + } + + vector> freqVec(freqMp.begin(), freqMp.end()); + sort(freqVec.begin(), freqVec.end(), cmp); + + string res; + for (auto i : freqVec) { + for (int j = 1; j <= i.second; j++) { + res += i.first; + } + } + return res; + } +}; \ No newline at end of file diff --git a/Topic6_Sorting/Sorting-HMT/9_SortList.txt b/Topic6_Sorting/Sorting-HMT/9_SortList.txt new file mode 100644 index 0000000..2d8ae71 --- /dev/null +++ b/Topic6_Sorting/Sorting-HMT/9_SortList.txt @@ -0,0 +1,67 @@ +/** + * Definition for singly-linked list. + * struct ListNode { + * int val; + * ListNode *next; + * ListNode() : val(0), next(nullptr) {} + * ListNode(int x) : val(x), next(nullptr) {} + * ListNode(int x, ListNode *next) : val(x), next(next) {} + * }; + */ + +// Time complexity: O(n*logn) +// Space complexity: O(n) - 2 additional ListNode left, right +// Explanation: Using Merge Sort + +class Solution { +public: + ListNode* sortList(ListNode* head) { + if (!head || !head->next) return head; + + ListNode* temp = NULL; + ListNode* slow = head; + ListNode* fast = head; + + // Find mid node using tortoise and hare algorithm + while(fast != NULL && fast->next != NULL){ + temp = slow; + slow = slow->next; + fast = fast->next->next; + } + + temp->next = NULL; + + // Recursively divide into 2 list node + ListNode* left = sortList(head); + ListNode* right = sortList(slow); + + return mergeList(left, right); + } + ListNode* mergeList(ListNode* left, ListNode* right){ + ListNode* dummy = new ListNode(); + ListNode* curr = dummy; + + while(left != NULL && right != NULL){ + if(left->val <= right->val){ + curr->next = left; + left = left->next; + }else{ + curr->next = right; + right = right->next; + } + curr = curr->next; + } + + while(left != NULL){ + curr->next = left; + left = left->next; + curr = curr->next; + } + while(right != NULL){ + curr->next = right; + right = right->next; + curr = curr->next; + } + return dummy->next; + } +}; \ No newline at end of file diff --git a/Topic7_HashTable/HashTable-HMTuan/1_MaxNumberOfKSumPairs.cpp b/Topic7_HashTable/HashTable-HMTuan/1_MaxNumberOfKSumPairs.cpp new file mode 100644 index 0000000..bd76be8 --- /dev/null +++ b/Topic7_HashTable/HashTable-HMTuan/1_MaxNumberOfKSumPairs.cpp @@ -0,0 +1,21 @@ +// Time complexity: O(n) +// Space complexity: O(n) +// Explanation: Using hash map to store the frequency of element +// Decrease the frequency if there is a k-sum pair + +class Solution { +public: + int maxOperations(vector& nums, int k) { + unordered_map freqMp; + int maxOps = 0; + for(int num : nums){ + if(freqMp[k-num] > 0){ + ++maxOps; + freqMp[k-num]--; + }else{ + freqMp[num]++; + } + } + return maxOps; + } +}; diff --git a/Topic7_HashTable/HashTable-HMTuan/3_ContinuousSubarraySum.cpp b/Topic7_HashTable/HashTable-HMTuan/3_ContinuousSubarraySum.cpp new file mode 100644 index 0000000..6eb5fad --- /dev/null +++ b/Topic7_HashTable/HashTable-HMTuan/3_ContinuousSubarraySum.cpp @@ -0,0 +1,32 @@ +// Time complexity: O(n) +// Space complexity: O(n) +// Explanation: Using hash map and prefixSum +// *note: initial case where prefixSum = 0 at index -1 + +class Solution { +public: + bool checkSubarraySum(vector& nums, int k) { + // prefixMod store pairs + unordered_map prefixMod; + int sum = 0; + + // Handle the case where prefixSum = 0 at index -1 + // Ex: nums: 1 4 3 1 3 k = 6 + // prefixSum: 0 1 5 8 9 12 + // prefixMod: 0 1 5 2 3 0 + prefixMod[0] = -1; + + for(int i = 0; i < nums.size(); i++){ + sum += nums[i]; + int remainder = sum % k; + if(prefixMod.find(remainder) != prefixMod.end()){ + // Make sure that the subarray length is at least 2 + if(i > prefixMod[remainder] + 1) + return true; + }else{ + prefixMod[remainder] = i; + } + } + return false; + } +}; \ No newline at end of file diff --git a/Topic7_HashTable/HashTable-HMTuan/5_IntervalsBetweenIdenticalElements.cpp b/Topic7_HashTable/HashTable-HMTuan/5_IntervalsBetweenIdenticalElements.cpp new file mode 100644 index 0000000..67848ac --- /dev/null +++ b/Topic7_HashTable/HashTable-HMTuan/5_IntervalsBetweenIdenticalElements.cpp @@ -0,0 +1,39 @@ +// Time complexity: O(n) +// Space complexity: O(n) - vector result(arr.size()); +// Explanation: Using hash map(store frequency and index) and prefixSum +// Using sum, prefixSum, len to find the result[idx] where idx: index in vector arr +// Because, It will take time if you calculate diff of index of identical element + +class Solution { +public: + vector getDistances(vector& arr) { + // freqMp stores index of identical element>> pairs + unordered_map> freqMp; + for(int i = 0; i < arr.size(); i++){ + vector& freqVec = freqMp[arr[i]]; + freqVec.push_back(i); + } + + vector result(arr.size()); + for(auto entry : freqMp){ + int len = entry.second.size(); + long long sum = 0; + for(int idx : entry.second) + sum += idx; + + long long prefixSum = 0; + // i: index in vector index of identical element corresponding to frequency of + for(long long i = 0; i < len; i++){ + // idx: index in vector arr + int idx = entry.second[i]; + + // result = idx * i - left side (prefix sum) + + // right side (sum - prefixSum - idx) - - (len - i - 1) * idx + result[idx] = idx * i - prefixSum + + (sum - prefixSum - idx) - (len - i - 1) * idx; + prefixSum += idx; + } + } + return result; + } +}; \ No newline at end of file diff --git a/Topic7_HashTable/HashTable-HMTuan/6_ValidAnagram.cpp b/Topic7_HashTable/HashTable-HMTuan/6_ValidAnagram.cpp new file mode 100644 index 0000000..612610d --- /dev/null +++ b/Topic7_HashTable/HashTable-HMTuan/6_ValidAnagram.cpp @@ -0,0 +1,22 @@ +// Time complexity: O(n) +// Space complexity: O(n) +// Explanation: Using hash map to store frequency +// Then, check if 2 map is the same + +class Solution { +public: + bool isAnagram(string s, string t) { + if(s.size() != t.size()) + return false; + unordered_map sMap; + unordered_map tMap; + + for(int i = 0; i < s.size(); i++){ + sMap[s[i]]++; + tMap[t[i]]++; + } + if(sMap == tMap) + return true; + return false; + } +}; \ No newline at end of file diff --git a/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/1_LowestCommonAncestorOfABinaryTree.txt b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/1_LowestCommonAncestorOfABinaryTree.txt new file mode 100644 index 0000000..96ac16d --- /dev/null +++ b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/1_LowestCommonAncestorOfABinaryTree.txt @@ -0,0 +1,30 @@ +/** + * Definition for a binary tree node. + * struct TreeNode { + * int val; + * TreeNode *left; + * TreeNode *right; + * TreeNode(int x) : val(x), left(NULL), right(NULL) {} + * }; + */ + +// Time complexity: O(n) +// Space complexity: O(h) - height of the Binary Tree +// Explanation: Using recursive DFS + +class Solution { +public: + TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) { + if (!root || root == p || root == q) return root; + + TreeNode* left = lowestCommonAncestor(root->left, p, q); + TreeNode* right = lowestCommonAncestor(root->right, p, q); + + // this is the lowest common ancestor + if (left && right) return root; + + // if node found return it here propegate + // it up till we find the other node + return left ? left : right; + } +}; \ No newline at end of file diff --git a/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/2_PathSum.txt b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/2_PathSum.txt new file mode 100644 index 0000000..11547a3 --- /dev/null +++ b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/2_PathSum.txt @@ -0,0 +1,33 @@ +/** + * 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) {} + * }; + */ + +// Time complexity: O(n) +// Space complexity: O(h) - height of the Tree +// Explanation: Using recursive dfs + +class Solution { +public: + bool hasPathSum(TreeNode* root, int targetSum) { + // checking TreeNode is null or not + if(!root) return false; + + // return true if the current targetSum = leaf node value + if(root->left == NULL && root->right == NULL) + return targetSum == root->val; + + bool leftSum = hasPathSum(root->left, targetSum - root->val); + bool rightSum = hasPathSum(root->right, targetSum - root->val); + + // if leftSum or rightSum is true -> func is true + return leftSum || rightSum; + } +}; \ No newline at end of file diff --git a/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/4_RangeSumOfBST.txt b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/4_RangeSumOfBST.txt new file mode 100644 index 0000000..12cf9ca --- /dev/null +++ b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/4_RangeSumOfBST.txt @@ -0,0 +1,29 @@ +/** + * 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) {} + * }; + */ + +// Time complexity: O(n) +// Space complexity: O(h) - height of the Binary Search Tree +// Explanation: Using recursive DFS (divide and conquer) + +class Solution { +public: + int rangeSumBST(TreeNode* root, int low, int high) { + if(!root) + return 0; + int left = rangeSumBST(root->left, low, high); + int right = rangeSumBST(root->right, low, high); + if(low <= root->val && root->val <= high){ + return root->val + left + right; + } + return left + right; + } +}; \ No newline at end of file diff --git a/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/5_ValidateBinarySearchTree.txt b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/5_ValidateBinarySearchTree.txt new file mode 100644 index 0000000..212e992 --- /dev/null +++ b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/5_ValidateBinarySearchTree.txt @@ -0,0 +1,35 @@ +/** + * 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) {} + * }; + */ + +// Time complexity: O(n) +// Space complexity: O(h) - height of the Tree +// Explanation: Using recursive dfs + +class Solution { +public: + bool isValidBST(TreeNode* root) { + return isValid(root, LONG_MIN, LONG_MAX); + } + + bool isValid(TreeNode* root, long min, long max){ + if(!root) return true; + + if(!(min < root->val && root->val < max)) + return false; + + // left subtree < node's key -> update max = node's value + bool left = isValid(root->left, min, root->val); + // right subtree > node's key -> update min = node's value + bool right = isValid(root->right, root->val, max); + return (left && right); + } +}; \ No newline at end of file diff --git a/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/6_BinarySearchTreeIterator.txt b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/6_BinarySearchTreeIterator.txt new file mode 100644 index 0000000..96122ac --- /dev/null +++ b/Topic8_TreeDFSBFS/TreeDFSBFS_HMTuan/6_BinarySearchTreeIterator.txt @@ -0,0 +1,48 @@ +/** + * 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) {} + * }; + */ + +// Time complexity: next() - O(h); hasNext - O(1) +// Space complexity: O(1) +// Explanation: Push all left node whenever possible and backtrack to parent node + +class BSTIterator { + +public: + stack treeSt; + BSTIterator(TreeNode* root) { + pushAllLeft(root); + } + + void pushAllLeft(TreeNode* node){ + for(; node != NULL; node = node->left){ + treeSt.push(node); + } + } + + int next() { + TreeNode* temp = treeSt.top(); + treeSt.pop(); + pushAllLeft(temp->right); + return temp->val; + } + + bool hasNext() { + return !treeSt.empty(); + } +}; + +/** + * Your BSTIterator object will be instantiated and called as such: + * BSTIterator* obj = new BSTIterator(root); + * int param_1 = obj->next(); + * bool param_2 = obj->hasNext(); + */ \ No newline at end of file