Feb 2023 Daily Leetcode: Leetcode in C++/RUST
48. Sort List
- Difficulty: Medium
- Link: 48. Sort List
Description
Given the head
of a linked list, return the list after sorting it in ascending order.
Solution
Merge sort is constantly used when it comes to sort a linked list. This question also appears in my algorithm course's final exam.
- Runtime:
O(nlgn)
- Space:
O(lgn)
- O(lgn) is the call graph
1/** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode() : val(0), next(nullptr) {} 7 * ListNode(int x) : val(x), next(nullptr) {} 8 * ListNode(int x, ListNode *next) : val(x), next(next) {} 9 * }; 10 */ 11class Solution { 12public: 13 ListNode* sortList(ListNode* head) { 14 // empty list or only one element 15 if(!head||!head->next) 16 return head; 17 ListNode* mid = split(head); 18 ListNode* left = sortList(head); 19 ListNode* right = sortList(mid); 20 21 return merge(left,right); 22 } 23 24 // famous two finger algorithm 25 ListNode* merge(ListNode* f1, ListNode* f2){ 26 ListNode* sentinel = new ListNode(0); 27 ListNode* it = sentinel; 28 29 while(f1&&f2){ 30 if(f1->val < f2->val) { 31 it->next = f1; 32 f1 = f1->next; 33 } 34 else{ 35 it->next = f2; 36 f2 = f2->next; 37 } 38 it = it->next; 39 } 40 41 while(f1){ 42 it->next = f1; 43 f1 = f1->next; 44 it = it->next; 45 } 46 47 while(f2){ 48 it->next = f2; 49 f2 = f2->next; 50 it = it->next; 51 } 52 53 return sentinel->next; 54 } 55 56 ListNode* split(ListNode* head) { 57 ListNode *midPrev = nullptr; 58 while(head && head->next) { 59 midPrev = (midPrev == nullptr) ? head : midPrev->next; 60 head = head->next->next; 61 } 62 ListNode* mid = midPrev->next; 63 // detach the linked list into two halves 64 midPrev->next = nullptr; 65 66 // return the second half, i.e, the detached 67 return mid; 68 } 69};
2131. Longest Palindrome by Concatenating Two Letter Words
- Difficulty: Medium
- Link: 48. Sort List
Description
You are given an array of strings words
. Each element of words
consists of two lowercase English letters.
Create the longest possible palindrome by selecting some elements from words
and concatenating them in any order. Each element can be selected at most once.
Return the length of the longest palindrome that you can create. If it is impossible to create any palindrome, return 0
.
A palindrome is a string that reads the same forward and backward.
Solution
- Runtime:
O(n)
- Space:
O(n)
1class Solution { 2public: 3 int longestPalindrome(vector<string>& words) { 4 unordered_map<string, int> table; 5 int result = 0; 6 for(auto str: words){ 7 string rev(1,str[1]); 8 rev+=str[0]; 9 if(table[rev]>0){ 10 table[rev]-=1; 11 result+=4; 12 } 13 else{ 14 table[str]+=1; 15 } 16 } 17 for(auto it:table){ 18 if(it.second>0&&it.first[0]==it.first[1]){ 19 result+=2; 20 return result; 21 } 22 } 23 24 return result; 25 } 26};
621. Task Scheduler
- Difficulty: Medium
- Link: 621. Task Scheduler
Description
Given a characters array tasks
, representing the tasks a CPU needs to do, where each letter represents a different task. Tasks could be done in any order. Each task is done in one unit of time. For each unit of time, the CPU could complete either one task or just be idle.
However, there is a non-negative integer n
that represents the cooldown period between two same tasks
(the same letter in the array), that is that there must be at least n units of time between any two same tasks.
Return the least number of units of times that the CPU will take to finish all the given tasks.
Solution
- Runtime:
O(n)
- Space:
O(1)
: O(26) to be precise.
1class Solution { 2public: 3 int leastInterval(vector<char>& tasks, int n) { 4 unordered_map<char, int> table; 5 for(char &c:tasks){ 6 table[c]++; 7 } 8 9 int time = 0; 10 11 // greedy algorithm, the most heavy tasks goes first, use a max heap to retrieve the heavies taks in log26 12 priority_queue<int> readyQueue; 13 for(auto it:table){ 14 if(it.second>0) 15 readyQueue.push(it.second); 16 } 17 18 // (a, b): - a is the remaining cycles needed to complete this task 19 // - b is the next time this task can be executed 20 queue<pair<int,int>> waitQueue; 21 22 // if there is tasks remaining 23 while(!readyQueue.empty() || !waitQueue.empty()){ 24 time++; 25 // if there is work in ready queue 26 if(!readyQueue.empty()){ 27 // remove the task 28 int occur = readyQueue.top(); 29 readyQueue.pop(); 30 if(occur-1>0) 31 // move the task to waitQueue, and "do" 1 cycle's work 32 waitQueue.push({occur-1,time+n}); 33 } 34 35 // if there is work in the waitQueue 36 if(!waitQueue.empty()){ 37 // the front() would always has the smallest wake time 38 // check if we can wake up the earliest task 39 if(waitQueue.front().second<=time){ 40 // wake up the task 41 readyQueue.push(waitQueue.front().first); 42 waitQueue.pop(); 43 } 44 } 45 } 46 47 return time; 48 } 49};
543. Diameter of Binary Tree
- Difficulty: Easy
- Link: 543. Diameter of Binary Tree
Description
Given the root
of a binary tree, return the length of the diameter of the tree.
The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root
.
The length of a path between two nodes is represented by the number of edges between them.
Solution
- Runtime:
O(n)
- Space:
O(n)
: call stack if the tree is a straight line
1/** 2* Definition for a binary tree node. 3* struct TreeNode { 4* int val; 5* TreeNode *left; 6* TreeNode *right; 7* TreeNode() : val(0), left(nullptr), right(nullptr) {} 8* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 9* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 10* }; 11*/ 12class Solution { 13public: 14 int diameterOfBinaryTree(TreeNode* root) { 15 int maxD = 0; 16 diamaterHelper(root, maxD); 17 return maxD; 18 } 19 20 21 int diameterHelper(TreeNode* root, int &maxD){ 22 if(root==nullptr){ 23 return 0; 24 } 25 26 int left = diameterHelper(root->left, maxD); 27 int right = diameterHelper(root->right, maxD); 28 29 30 // two cases, the maximum is the left height + right height 31 // tree rooted at this node is not optimal, pass up the height 32 if(left+right>maxD) 33 maxD = left+right 34 return max(left,right)+1; 35 } 36};
437. Path Sum III
- Difficulty: Medium
- Link: 437. Path Sum III
Description
Given the root
of a binary tree and an integer targetSum
, return the number of paths where the sum of the values along the path equals targetSum
.
The path does not need to start or end at the root or a leaf, but it must go downwards (i.e., traveling only from parent nodes to child nodes).
Solution
- Runtime:
O(n^2)
- Space:
O(n)
: call stack if the tree is a straight line
1/** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode() : val(0), left(nullptr), right(nullptr) {} 8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 10 * }; 11 */ 12class Solution { 13public: 14 int pathSum(TreeNode* root, int targetSum) { 15 if(root==nullptr){ 16 return 0; 17 } 18 // consider two cases 19 // 1: the root is included in the path -> pathSumInclude 20 // 2: the root is not included in the path -> pathSum 21 return pathSumInclude(root, targetSum)+pathSum(root->left,targetSum)+pathSum(root->right,targetSum); 22 } 23 24 int pathSumInclude(TreeNode* root, long targetSum) { 25 if(root==nullptr){ 26 return 0; 27 } 28 // if we find what we wanted, add 1 29 if(root->val==targetSum){ 30 // but we still need to recurse because there might be nodes added up to 0 31 return 1+pathSumInclude(root->left, targetSum-root->val)+pathSumInclude(root->right, targetSum-root->val); 32 } 33 else{ 34 // we need to recurse using pathSumInclude because the path should be continous 35 return pathSumInclude(root->left, targetSum-root->val)+pathSumInclude(root->right, targetSum-root->val); 36 } 37 } 38};
74. Search a 2D Matrix
- Difficulty: Medium
- Link: 74. Search a 2D Matrix
Description
You are given an m x n
integer matrix matrix with the following two properties:
Each row is sorted in non-decreasing order. The first integer of each row is greater than the last integer of the previous row.
Given an integer target, return true
if target
is in matrix
or false
otherwise.
You must write a solution in O(log(m * n))
time complexity.
Solution
If you flatten the matrix in to an one-dimension array with size of m*n
,
the 1d array will be in non decreasing order. Thus, we can perform binary
search on the 1d array. The mid
indice translation is matrix[mid/n][mid%n]
- Runtime:
O(lg(m*n))
- Space:
O(1)
1class Solution { 2public: 3 bool searchMatrix(vector<vector<int>>& matrix, int target) { 4 int m = matrix.size(), n = matrix[0].size(); 5 int left = 0, right = m*n-1; 6 7 while(left < right){ 8 int mid = (left+right)/2; 9 if(matrix[mid/n][mid%n]<target){ 10 left = mid+1; 11 } 12 else if (matrix[mid/n][mid%n]>target) 13 { 14 right = mid-1; 15 } 16 else{ 17 return true; 18 } 19 } 20 return matrix[left/n][left%n]==target; 21 } 22};
33. Search in Rotated Sorted Array
- Difficulty: Medium
- Link: 33. Search in Rotated Sorted Array
Description
There is an integer array nums
sorted in ascending order (with distinct values).
Prior to being passed to your function, nums
is possibly rotated at an unknown pivot index k
(1 <= k < nums.length
) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
(0-indexed). For example, [0,1,2,4,5,6,7]
might be rotated at pivot index 3 and become [4,5,6,7,0,1,2]
.
Given the array nums
after the possible rotation and an integer target
, return the index of target
if it is in nums
, or -1
if it is not in nums
.
You must write an algorithm with O(log n)
runtime complexity.
Solution
The only difference between this problem with ordinary binary search is there is a pivot point where the ascending ordered list is rotated. If we can find the pivot point, the problem just rolls back to a binary search problem.
- Runtime:
O(lgn)
- Space:
O(1)
1class Solution { 2public: 3 int search(vector<int>& nums, int target) { 4 int low = 0, high = nums.size() - 1; 5 6 // find the smallest element => pivot point 7 while (low < high) 8 { 9 int mid = (low + high) / 2; 10 11 if (nums[mid] > nums[high]) 12 { 13 low = mid + 1; 14 } 15 else 16 { 17 high = mid ; 18 } 19 } 20 // find the point that is the pivot 21 int pivot = low; 22 23 low = 0, high = nums.size() - 1; 24 // find the element 25 while (low <= high) 26 { 27 int mid = (low + high) / 2; 28 int realmid = (mid + pivot) % nums.size(); 29 30 if (nums[realmid] == target) 31 { 32 return realmid; 33 } 34 35 if (nums[realmid] < target) 36 { 37 low = mid+1; 38 } 39 else{ 40 high = mid-1; 41 } 42 } 43 44 return -1; 45 } 46};
108. Convert Sorted Array to Binary Search Tree
- Difficulty: Easy
- Link: 108. Convert Sorted Array to Binary Search Tree
Description
Given an integer array nums
where the elements are sorted in ascending order, convert it to a height-balanced binary search tree.
Solution
- Runtime:
O(n)
- Space:
O(1)
1/** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode() : val(0), left(nullptr), right(nullptr) {} 8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 10 * }; 11 */ 12class Solution { 13public: 14 TreeNode* sortedArrayToBST(vector<int>& nums) { 15 if(nums.size()>0){ 16 TreeNode *sentinel = new TreeNode(); 17 // build the tree rooted the sentinel's left child 18 helper(sentinel, nums, 0, nums.size()-1, true); 19 return sentinel->left; 20 } 21 return nullptr; 22 } 23 24 void helper(TreeNode* parent,vector<int>& nums, int left, int right,bool leftChild ){ 25 26 if(left<=right){ 27 // get the mid element 28 int mid = (left+right)/2; 29 // create new node 30 TreeNode *temp = new TreeNode(nums[mid]); 31 // attach to parent's left 32 if(leftChild){ 33 parent->left = temp; 34 } // attach to parent's right 35 else { 36 parent->right = temp; 37 } 38 // recursive build subtrees 39 helper(temp, nums, left, mid-1, true); 40 helper(temp, nums, mid+1, right, false); 41 } 42 } 43};
230. Kth Smallest Element in a BST
- Difficulty: Medium
- Link: 230. Kth Smallest Element in a BST
Description
Given the root
of a binary search tree, and an integer k
, return the kth
smallest value (1-indexed) of all the values of the nodes in the tree.
Solution
- Runtime:
O(n)
- Space:
O(n)
1/** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode() : val(0), left(nullptr), right(nullptr) {} 8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 10 * }; 11 */ 12class Solution { 13public: 14 int kthSmallest(TreeNode* root, int k) { 15 // this could be done in an inorder traversal 16 // inorder traversal is a form of DFS 17 // use stack to carry the dfs 18 stack<TreeNode*> stack; 19 20 while(!stack.empty()||root){ 21 // keep descend to the most left leaf 22 while(root){ 23 stack.push(root); 24 root = root -> left; 25 } 26 // traversal backwards 27 root = stack.top(); 28 stack.pop(); 29 30 if(--k==0){ 31 // if the current is kth smallest, return 32 return root->val; 33 } 34 else{ 35 // traversal on the right substree 36 root = root ->right; 37 } 38 } 39 return -1; 40 } 41};
173. Binary Search Tree Iterator
- Difficulty: Medium
- Link: 173. Binary Search Tree Iterator
Description
Implement the BSTIterator
class that represents an iterator over the in-order traversal of a binary search tree (BST):
BSTIterator(TreeNode root)
Initializes an object of the BSTIterator
class. The root
of the BST is given as part of the constructor. The pointer should be initialized to a non-existent number smaller than any element in the BST.
boolean hasNext()
Returns true
if there exists a number in the traversal to the right of the pointer, otherwise returns false.
int next()
Moves the pointer to the right, then returns the number at the pointer.
Notice that by initializing the pointer to a non-existent smallest number, the first call to next()
will return the smallest element in the BST.
You may assume that next()
calls will always be valid. That is, there will be at least a next number in the in-order traversal when next()
is called.
Solution
- Runtime:
O(1)
: each operation is constant - Space:
O(n)
1/** 2 * Definition for a binary tree node. 3 * struct TreeNode { 4 * int val; 5 * TreeNode *left; 6 * TreeNode *right; 7 * TreeNode() : val(0), left(nullptr), right(nullptr) {} 8 * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} 9 * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} 10 * }; 11 */ 12class BSTIterator { 13private: 14 vector<int> nums; 15 int ptr = 0; 16 17 void inorder(TreeNode* root){ 18 if(root){ 19 inorder(root->left); 20 nums.push_back(root->val); 21 inorder(root->right); 22 } 23 } 24 25public: 26 BSTIterator(TreeNode* root) { 27 inorder(root); 28 } 29 30 int next() { 31 return nums[ptr++]; 32 33 } 34 35 bool hasNext() { 36 return ptr < nums.size(); 37 } 38}; 39 40/** 41 * Your BSTIterator object will be instantiated and called as such: 42 * BSTIterator* obj = new BSTIterator(root); 43 * int param_1 = obj->next(); 44 * bool param_2 = obj->hasNext(); 45 */
994. Rotting Oranges
- Difficulty: Medium
- Link: 994. Rotting Oranges
Description
You are given an m x n
grid
where each cell can have one of three values:
0
representing an empty cell,
1
representing a fresh orange, or
2
representing a rotten orange.
Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten.
Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1
.
Solution
- Runtime:
O(mn)
- Space:
O(mn)
1class Solution { 2public: 3 int orangesRotting(vector<vector<int>>& grid) { 4 int m = grid.size(); 5 int n = grid[0].size(); 6 queue<pair<int,int>> rotten; 7 int fresh = 0; 8 // find the intiial rotten oranges 9 // count the initial number of fresh oranges 10 for(int i=0; i<m; i++){ 11 for(int j=0; j<n; j++){ 12 if(grid[i][j]==1){ // fresh oranges 13 fresh++; 14 } 15 else if(grid[i][j]==2){ // rotten oranges 16 rotten.push({i,j}); 17 // grid[i][j] = -1; 18 } 19 } 20 } 21 int mins = 0; 22 23 vector<pair<int,int>> directions = {{0,1},{0,-1},{1,0},{-1,0}}; 24 25 while(!rotten.empty() && fresh>0){ 26 int times = rotten.size(); 27 // take a snapshot of the queue at this minute 28 29 for(int k=0; k<times; k++){ 30 auto ro = rotten.front(); 31 rotten.pop(); 32 for(auto dir : directions) { 33 int row = ro.first + dir.first; 34 int col = ro.second + dir.second; 35 if(row >=0 && row<m && col>=0 && col<n && grid[row][col]==1){ 36 grid[row][col]=2; 37 rotten.push({row,col}); 38 fresh--; 39 } 40 } 41 } 42 mins++; 43 } 44 45 return fresh>0?-1:mins; 46 } 47};
417. Pacific Atlantic Water Flow
- Difficulty: Medium
- Link: 417. Pacific Atlantic Water Flow
Description
There is an m x n
rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island's left and top edges, and the Atlantic Ocean touches the island's right and bottom edges.
The island is partitioned into a grid of square cells. You are given an m x n
integer matrix heights
where heights[r][c]
represents the height above sea level of the cell at coordinate (r, c)
.
The island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell's height is less than or equal to the current cell's height. Water can flow from any cell adjacent to an ocean into the ocean.
Return a 2D list of grid coordinates result
where result[i] = [ri, ci]
denotes that rain water can flow from cell (ri, ci)
to both the Pacific and Atlantic oceans.
Solution
<!-- - Runtime: `O()` - Space: `O()` -->1class Solution { 2public: 3 vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) { 4 5 } 6};
210. Course Schedule II
- Difficulty: Medium
- Link: 210. Course Schedule II
Description
There are a total of numCourses
courses you have to take, labeled from 0
to numCourses - 1
. You are given an array prerequisites
where prerequisites[i] = [ai, bi]
indicates that you must take course bi
first if you want to take course ai
.
For example, the pair [0, 1]
, indicates that to take course 0
you have to first take course 1
.
Return the ordering of courses you should take to finish all courses. If there are many valid answers, return any of them. If it is impossible to finish all courses, return an empty array.
Solution
- Runtime:
O()
- Space:
O()