跳至主要内容

268 篇文章 含有標籤「leetcode」

檢視所有標籤

146. LRU Cache

· 閱讀時間約 3 分鐘

Hint

struct Node {
// Constructor to initialize a node with key and value
};

class LRUCache {
private:
// Capacity of the cache

// Hash map to store key to node mappings

// Dummy head and tail nodes for the doubly linked list

// Helper function to remove a node from the doubly linked list
void remove()
{
}

// Helper function to add a node to the end (before the tail) of the doubly linked list
void add()
{
}

public:
// Constructor to initialize the LRU Cache with given capacity
LRUCache(int capacity)
{
}

// Destructor to clean up all nodes
~LRUCache()
{
}

// Function to get the value of a key
int get(int key)
{
// If key is not found, return -1

// Move the accessed node to the end (most recently used)

// Return the value associated with the key
}

// Function to put a key-value pair in the cache
void put(int key, int value)
{
// If key already exists, remove the existing node

// Create a new node for the key-value pair

// If the cache exceeds capacity, remove the least recently used (LRU) node
if ()
{
}
}
};

/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/

struct Node {
int key;
int val;
Node* next;
Node* prev;
Node(int key, int val) : key(key), val(val), next(nullptr), prev(nullptr) {}
};

class LRUCache {
private:
int cap;

unordered_map<int, Node*> m;

Node* head = new Node(-1, -1);
Node* tail = new Node(-1, -1);

// head <-> 1 <-> 2 <-> 3 <-> tail
// ^ node
// ^ prev_node
// ^ next_node
void remove(Node* node)
{
Node* prev_node = node->prev;
Node* next_node = node->next;

prev_node->next = next_node;
next_node->prev = prev_node;
}

// head <-> 1 <-> 2 <-> tail
// ^ prev_node
// ^ next_node
void add(Node* node)
{
Node* prev_node = tail->prev;
Node* next_node = tail;

prev_node->next = node;
next_node->prev = node;

node->next = next_node;
node->prev = prev_node;
}

public:
LRUCache(int capacity)
{
cap = capacity;
// head <-> tail
head->next = tail;
tail->prev = head;
}

~LRUCache()
{
Node* cur = head;
while (cur) {
Node* next = cur->next;
delete cur;
cur = next;
}
}

int get(int key)
{
if(!m.count(key)) return -1;
Node* node_to_get = m[key];
remove(node_to_get);
add(node_to_get);
return node_to_get->val;
}

void put(int key, int value)
{
if(m.count(key)) remove(m[key]);

Node* node_to_put = new Node(key, value);
m[key] = node_to_put;
add(node_to_put);

if (m.size() > cap)
{
Node* node_to_delete = head->next;
remove(node_to_delete);
m.erase(node_to_delete->key);
delete node_to_delete;
}
}
};

/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache* obj = new LRUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
  • T: $O(1)$
  • S: $O(capacity)$

145. Binary Tree Postorder Traversal

· 閱讀時間約 1 分鐘

recursion

/**
* 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 {
private:
vector<int> res;
public:
vector<int> postorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root) {
if(!root) return;
dfs(root->left);
dfs(root->right);
res.push_back(root->val);
}
};
  • T: $O(N)$
  • S: $O(N)$

iteration

/**
* 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<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if(!root) return res;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top(); stk.pop();
res.push_back(node->val);
if(node->left) {
stk.push(node->left);
}
if(node->right) {
stk.push(node->right);
}
}
reverse(res.begin(), res.end());
return res;
}
};
  • T: $O(N)$
  • S: $O(N)$

144. Binary Tree Preorder Traversal

· 閱讀時間約 1 分鐘

recursion

/**
* 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 {
private:
vector<int> res;
public:
vector<int> preorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root) {
if(!root) return;
res.push_back(root->val);
dfs(root->left);
dfs(root->right);
}
};
  • T: $O(N)$
  • S: $O(N)$

iteration

/**
* 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<int> preorderTraversal(TreeNode* root) {
// preorder: root -> left ->right
vector<int> res;
stack<TreeNode*> stk;
stk.push(root);

while(!stk.empty()) {
TreeNode* node = stk.top(); stk.pop();
if(!node) continue;
res.push_back(node->val);
// 為了先處理左側,先 push 右側到 stack
if(node->right) {
stk.push(node->right);
}
// 利用 stack 的特性,最後再 push 左側
if(node->left) {
stk.push(node->left);
}
}
return res;
}
};
  • T: $O(N)$
  • S: $O(N)$

143. Reorder List

· 閱讀時間約 2 分鐘

You are given the head of a singly linked-list. The list can be represented as:

L0 → L1 → … → Ln - 1 → Ln

Reorder the list to be on the following form:

L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

You may not modify the values in the list's nodes. Only nodes themselves may be changed.

Example 1:

image

Input: head = [1,2,3,4] Output: [1,4,2,3]

Example 2:

image

Input: head = [1,2,3,4,5] Output: [1,5,2,4,3]

Constraints:

  • The number of nodes in the list is in the range [1, 5 * 104].
  • 1 <= Node.val <= 1000
/**
* 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) {}
* };
*/
class Solution {
public:
void reorderList(ListNode* head) {
// 使用快慢指針尋找中間節點
// 快指針每次移動兩步,慢指針每次移動一步,當快指針到達鏈表末尾時,慢指針剛好到達中間節點。
ListNode *slow = head;
ListNode *fast = head->next;
while(fast && fast->next) {
slow = slow->next;
fast = fast->next->next;
}
// 將中間節點之後的 linked list 反轉,以便後續可以交錯合併。
ListNode* cur = slow->next;
ListNode* prev = nullptr;

while(cur) {
ListNode* temp = cur->next;
cur->next = prev;
prev = cur;
cur = temp;
}

// 斷開前後兩個部分
slow->next = nullptr;

// 將前半部分和反轉後的後半部分交錯合併。
ListNode *first = head;
second = prev;
while(second) {
ListNode* node1 = first->next;
ListNode* node2 = second->next;
first->next = second;
second->next = node1;
first = node1;
second = node2;
}
}
};
  • T: $O(N)$
  • S: $O(1)$

141. Linked List Cycle

· 閱讀時間約 1 分鐘
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head)
{
ListNode* slow = head;
ListNode* fast = head;
while (fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if (slow == fast) return true;
}
return false;
}
};
  • T: $O(N)$
  • S: $O(1)$

139. Word Break

· 閱讀時間約 4 分鐘

Hint - DP

class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict)
{
// Initialize a DP array to keep track of valid substrings

// Iterate over each position in the string
for ()
{
// Check each word in the dictionary
for ()
{
// Ensure that the current index is sufficient to accommodate the word

// Check if the current position `i` can be formed by the current word
// We also need to ensure that `dp[i - word.size()]` is true if `i` is not the start of the string
if ()
{
// Check if the substring matches the current word
if ()
{
// Mark the current position as reachable
// No need to check further words for this position
}
}
}
}
// Return whether the entire string can be segmented into dictionary words
}
};
  • DP
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict)
{
int n = s.size();
vector<bool> dp(n);
for (int i = 0; i < n; i++)
{
for (auto& word : wordDict)
{
if (i < word.size() - 1) continue;
if (i == word.size() - 1 || dp[i - word.size()])
{
if (s.substr(i - word.size() + 1, word.size()) == word)
{
dp[i] = true; break;
}
}
}
}
return dp.back();
}
};
  • T: $O(N \cdot M \cdot K)$
  • S: $O(N)$

Hint - Trie + Bottom-Up DP

class TrieNode {
public:
// Indicates whether the node represents the end of a word
// Array of child nodes, one for each letter of the alphabet

TrieNode()
{
// Initialize the node as not the end of a word
// Initialize all child pointers to nullptr
}
};

class Solution {
private:
// Root of the Trie

public:
bool wordBreak(string s, vector<string>& wordDict)
{
// Build the Trie from the wordDict
// Initialize a new Trie root
for ()
{
// Convert character to index (0 for 'a', 1 for 'b', ..., 25 for 'z')
// Create new TrieNode if needed
// Move to the child node
// Mark the end of the word
}

// Dynamic Programming array to store results of subproblems
// Initialize DP array with false

for ()
{
// Check if current position is valid (either start of string or can be reached by a valid word break)
if ()
{
for ()
{
// Break if the current character path does not exist in the Trie
// Move to the next TrieNode
// Mark as true if we reach the end of a valid word
}
}
}
// Return if the entire string can be segmented
}
};
  • Trie + Bottom-Up DP
class TrieNode {
public:
bool isWord;
TrieNode* child[26];
TrieNode()
{
isWord = false;
for (int i = 0; i < 26; ++i)
{
child[i] = nullptr;
}
}
};

class Solution {
private:
TrieNode* root = new TrieNode();
public:
bool wordBreak(string s, vector<string>& wordDict)
{
root = new TrieNode();
for (auto w : wordDict)
{
TrieNode* node = root;
for (auto c : w)
{
if (!node->child[c - 'a'])
{
node->child[c - 'a'] = new TrieNode();
}
node = node->child[c - 'a'];
}
node->isWord = true;
}

vector<bool> dp(s.size());
for (int i = 0; i < s.size(); i++)
{
if (i == 0 || dp[i - 1])
{
TrieNode* node = root;
for (int j = i; j < s.size(); j++)
{
char c = s[j];
if (!node->child[c - 'a'])
{
break;
}
node = node->child[c - 'a'];
if (node->isWord) dp[j] = true;
}
}
}
return dp[s.size() - 1];
}
};
  • Trie + Top-Down DP
class TrieNode {
public:
TrieNode* child[26];
bool isWord;
TrieNode()
{
isWord = false;
for (int i = 0; i < 26; ++i)
{
child[i] = nullptr;
}
}
};

class Solution {
private:
TrieNode* root;
int memo[300];
public:
bool wordBreak(string s, vector<string>& wordDict)
{
root = new TrieNode();
for (auto word: wordDict)
{
TrieNode* node = root;
for (auto c : word)
{
if (!node->child[c - 'a'])
{
node->child[c - 'a'] = new TrieNode();
}
node = node->child[c - 'a'];
}
node->isWord = true;
}
return dfs(s, 0);
}

bool dfs(string& s, int cur)
{
if (memo[cur] == 1) return false;

if (cur == s.size()) return true;

TrieNode* node = root;
for (int i = cur; i < s.size(); ++i)
{
if (node->child[s[i] - 'a'])
{
node = node->child[s[i] - 'a'];
if (node->isWord && dfs(s, i + 1))
{
return true;
}
}
else break;
}

memo[cur] = 1;
return false;
}
};

138. Copy List with Random Pointer

· 閱讀時間約 1 分鐘
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;

Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/

class Solution {
private:
unordered_map<Node*, Node*> oldToNew;
public:
Node* copyRandomList(Node* head)
{
return dfs(head);
}

Node* dfs(Node* node)
{
if (!node) return nullptr;
if (oldToNew.count(node)) return oldToNew[node];

// 如果沒有找到 node
oldToNew[node] = new Node(node->val);
oldToNew[node]->next = dfs(node->next);
oldToNew[node]->random = dfs(node->random);
return oldToNew[node];
}
};
  • T: $O(N)$
  • S: $O(N)$

136. Single Number

· 閱讀時間約 1 分鐘
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for (auto& num : nums)
res ^= num;
return res;
}
};
  • T: $O(N)$
  • S: $O(1)$

135. Candy

· 閱讀時間約 2 分鐘
/*
* @lc app=leetcode id=135 lang=cpp
*
* [135] Candy
*
* https://leetcode.com/problems/candy/description/
*
* algorithms
* Hard (43.78%)
* Likes: 8018
* Dislikes: 683
* Total Accepted: 610.6K
* Total Submissions: 1.4M
* Testcase Example: '[1,0,2]'
*
* There are n children standing in a line. Each child is assigned a rating
* value given in the integer array ratings.
*
* You are giving candies to these children subjected to the following
* requirements:
*
*
* Each child must have at least one candy.
* Children with a higher rating get more candies than their neighbors.
*
*
* Return the minimum number of candies you need to have to distribute the
* candies to the children.
*
*
* Example 1:
*
*
* Input: ratings = [1,0,2]
* Output: 5
* Explanation: You can allocate to the first, second and third child with 2,
* 1, 2 candies respectively.
*
*
* Example 2:
*
*
* Input: ratings = [1,2,2]
* Output: 4
* Explanation: You can allocate to the first, second and third child with 1,
* 2, 1 candies respectively.
* The third child gets 1 candy because it satisfies the above two
* conditions.
*
*
*
* Constraints:
*
*
* n == ratings.length
* 1 <= n <= 2 * 10^4
* 0 <= ratings[i] <= 2 * 10^4
*
*
*/

// @lc code=start
class Solution {
public:
int candy(vector<int>& ratings) {
int n = ratings.size();
vector<int> candy(n, 1);

for (int i = 1; i < n; ++i) {
if (ratings[i] > ratings[i - 1]) {
candy[i] = candy[i - 1] + 1;
}
}

for (int i = n - 2; i >= 0; --i) {
if (ratings[i] > ratings[i + 1]) {
candy[i] = max(candy[i], candy[i + 1] + 1);
}
}
return accumulate(candy.begin(), candy.end(), 0);
}
};
// @lc code=end
  • T: $O(n)$
  • S: $O(n)$