考 前 救 急
206. 反转链表
掌握两种写法即可
class Solution {
public:
//tail是指待反转的链表的下一个元素,prev是指待反转链表的前一个元素
// 迭代写法
ListNode* reverseList(ListNode* head, ListNode* prev, ListNode* tail) {
if(head == nullptr) return nullptr;
// tail指的是链表尾结点的下一个结点
ListNode* cur = head, *pre = prev;
while(cur != tail) {
ListNode* nxt = cur->next;
cur->next = pre;
pre = cur;
cur = nxt;
}
return pre;
}
// 递归写法
ListNode* reverseList(ListNode* head, ListNode* prev, ListNode* tail) {
if(head == tail || head->next == tail) return head;
ListNode* reverse_tail = head->next;
ListNode* reverse_head = reverseList(head->next, head, tail);
reverse_tail->next = head;
head->next = prev;
return reverse_head;
}
ListNode* reverseList(ListNode* head) {
return reverseList(head, nullptr, nullptr);
}
};
146. LRU缓存
关键在于操作时,添加(put)或查询(get)的数据被最新访问了,所以需要将其加入到最开始将其优先级升为最高。
使用双向链表ListNode
,同时有一个初始的哨兵头结点head
和哨兵尾结点tail
,便于插入新结点和弹出优先级最低的结点
使用hash表hash
,使得key对应的ListNode可以快速被定位到,ListNode中需要存储key,方便我们弹出优先级最低的元素时,也将其从hash表中删除。
需要一个cap
记录当前可以put
的元素个数
addListNode
用于将结点加入双向链表
removeListNode
用于将结点从双向链表中弹出
put
用于将新数据存储
get
用于快速获取元素,直接调用hash表即可
class LRUCache {
private:
struct ListNode {
int key;
int value;
ListNode* left;
ListNode* right;
ListNode(int key, int value) : key(key), value(value), left(nullptr), right(nullptr) {}
};
int cap;
ListNode* head;
ListNode* tail;
unordered_map<int, ListNode*> hash;
public:
LRUCache(int capacity) {
cap = capacity;
hash.clear();
head = new ListNode(-1, -1);
tail = new ListNode(-1, -1);
head->right = tail;
tail->left = head;
}
void removeListNode(ListNode* node) {
node->left->right = node->right;
node->right->left = node->left;
}
void addListNode(ListNode* node) {
node->right = tail;
node->left = tail->left;
tail->left->right = node;
tail->left = node;
}
int get(int key) {
// 不存在
if(!hash.count(key)) return -1;
// 获取结点node
ListNode* node = hash[key];
// 更新node的优先级
// 1. 将其从List中弹出
removeListNode(node);
// 2. 将其加入到链表尾
addListNode(node);
// 返回结果
return node->value;
}
void put(int key, int value) {
// 查看是否存在
if(hash.count(key)) {
// 已存在
ListNode* node = hash[key];
node->value = value;
removeListNode(node);
addListNode(node);
} else {
// 不存在
ListNode* node = new ListNode(key, value);
// 需要删除优先级最低的元素
if(cap <= 0) {
ListNode* delNode = head->right;
// 从双向链表中删除
removeListNode(delNode);
// 从hash表中删除
hash.erase(delNode->key);
cap += 1;
}
hash[key] = node;
addListNode(node);
cap -= 1;
}
}
};
3. 无重复字符的最长子串
双指针,关键点在于每次向右扩展,只有你扩展的那个字符是可能重复的,所以只需要移动左指针使得扩展的那个字符数量不重复即可。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int cnt[256] = {0};
int n = s.size();
int res = 0;
for(int r = 0, l = 0; r < n; ++r) {
cnt[s[r]] += 1;
while(l < r && cnt[s[r]] > 1) cnt[s[l]] -= 1, l += 1;
res = max(res, r - l + 1);
}
return res;
}
};
215. 数组中的第k个最大元素
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
// 借用priority_queue存储至多k个元素
priority_queue<int, vector<int>, greater<int>> q;
for(auto u : nums) {
if(q.size() < k) q.push(u);
else if(u > q.top()) q.pop(), q.push(u);
}
return q.top();
}
};
手写 p r i o r i t y _ q u e u e priority\_queue priority_queue
class PQ {
vector<int> q;
int n;
function<bool(int,int)> cmp;
void up(int u) {
while(u > 1 && cmp(q[u], q[u / 2])) {
swap(q[u], q[u / 2]);
u /= 2;
}
}
void down(int u) {
while(true) {
int t = u;
if(u * 2 <= n && cmp(q[u * 2], q[t])) t = u * 2;
if(u * 2 + 1 <= n && cmp(q[u * 2 + 1], q[t])) t = u * 2 + 1;
if(u != t) {
swap(q[u], q[t]);
u = t;
} else break;
}
}
public:
PQ(function<bool(int,int)> f) {
cmp = f;
n = 0;
while(!q.empty()) q.pop_back();
q.push_back(-1);
}
int top() { return q[1]; }
void pop() {
if(q.size() <= 0) return ;
swap(q[1], q[n]);
q.pop_back();
n -= 1;
down(1);
}
void push(int x) {
q.push_back(x);
n += 1;
up(n);
}
int size() { return n; }
};
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
PQ q([](int a, int b) {return a < b;});
for(auto u : nums) {
if(q.size() < k) q.push(u);
else if(q.top() < u) q.pop(), q.push(u);
}
return q.top();
}
};
215. K个一组翻转链表
用到206的模板了,但是需要做点修改
/**
* 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:
ListNode* reverseList(ListNode* head, ListNode* prev, ListNode* tail) {
if(head == tail || head->next == tail) return head;
ListNode* cur = head, *pre = tail;
while(cur != tail) {
ListNode* nxt = cur->next;
cur->next = pre;
pre = cur;
cur = nxt;
}
// 前一个序列的尾元素,当前序列需要接在其之后
prev->next = pre;
return prev;
}
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* dummy = new ListNode(-1, head);
ListNode* first = head;
ListNode* second = head;
ListNode* prev = dummy;
while(second != nullptr) {
int tk = k;
while(tk > 0 && second != nullptr) {
second = second->next;
tk -= 1;
}
if(tk == 0) reverseList(first, prev, second);
prev = first; // first在翻转后成为当前序列的尾元素
first = second; // first更新为下一个序列的头元素
}
return dummy->next;
}
};
912. 排序数组
class Solution {
private:
void quick_sort(vector<int>& nums, int l, int r) {
if(l >= r) return ;
int x = nums[l + r >> 1];
int i = l - 1, j = r + 1;
while(i < j) {
do i++; while(nums[i] < x);
do j--; while(nums[j] > x);
if(i < j) swap(nums[i], nums[j]);
else quick_sort(nums, l, j), quick_sort(nums, j + 1, r);
}
}
vector<int> temp;
void merge_sort(vector<int>& nums, int l, int r) {
if(l >= r) return ;
int mid = l + r >> 1;
merge_sort(nums, l, mid);
merge_sort(nums, mid + 1, r);
int i = l, j = mid + 1, g = 0;
while(i <= mid && j <= r) {
if(nums[i] > nums[j]) temp[g] = nums[j], j += 1, g += 1;
else temp[g] = nums[i], i += 1, g += 1;
}
while(i <= mid) temp[g] = nums[i], i += 1, g += 1;
while(j <= r) temp[g] = nums[j], j += 1, g += 1;
for(i = l, j = 0; j < g; ++i, ++j)
nums[i] = temp[j];
}
public:
vector<int> sortArray(vector<int>& nums) {
quick_sort(nums, 0, (int)nums.size() - 1);
temp.resize(nums.size());
merge_sort(nums, 0, (int)nums.size() - 1);
return nums;
}
};
15. 三数之和
不可以包含重复的三元组,每次枚举 n u m s [ i ] nums[i] nums[i]必须不同,同时要满足在同一 n u m s [ i ] nums[i] nums[i]下的 n u m s [ j ] nums[j] nums[j]每次必须是不同的。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<vector<int>> res;
for(int i = 0; i < n; ++i) {
if(i > 0 && nums[i] == nums[i - 1]) continue ;
for(int j = i + 1, k = n - 1; j < k; ++j) {
if(j > i + 1 && nums[j] == nums[j - 1]) continue;
while(j < k && nums[i] + nums[j] + nums[k] > 0) k -= 1;
if(j < k && nums[i] + nums[j] + nums[k] == 0) {
res.push_back({nums[i], nums[j], nums[k]});
}
}
}
return res;
}
};
1. 两数之和
空间和时间只能选择一个
要么空间
O
(
n
)
O(n)
O(n)和时间
O
(
n
)
O(n)
O(n)
要么空间
O
(
1
)
O(1)
O(1)和实践
O
(
n
2
)
O(n^2)
O(n2)
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hash;
vector<int> res;
for(int i = 0; i < nums.size(); ++i) {
if(hash.count(target - nums[i])) {
res.push_back(i);
res.push_back(hash[target - nums[i]]);
return res;
} else {
hash[nums[i]] = i;
}
}
return res;
}
};
21. 合并两个有序链表
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* dummy = new ListNode(-1);
ListNode* temp = dummy;
while(list1 != nullptr && list2 != nullptr) {
if(list1->val < list2->val) temp->next = list1, list1 = list1->next;
else temp->next = list2, list2 = list2->next;
temp = temp->next;
}
if(list1 != nullptr) temp->next = list1;
if(list2 != nullptr) temp->next = list2;
return dummy->next;
}
};
2021.12.26 2021.12.26 2021.12.26