申明:以下所有内容均来自力扣
此博客就是总结了部分作者觉得好的题,难得题,需要品味的题,也是需要背诵的题。
此题没有明确说明,就是作者自己写的代码。
大家看了题没思路的话,自己点开,看题解。
本博客主要是方便复习和查看
第三天
3.1 寻找两个正序数组的中位数
4. 寻找两个正序数组的中位数https://leetcode-cn.com/problems/median-of-two-sorted-arrays/
给定两个大小分别为 m和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。
算法的时间复杂度应该为 O(log (m+n)) 。
示例 1:
输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:
输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
利用两个数组逐渐递增的性质,l指向nums1, l2指向nums2,判断两者的大小,一次写入数组record,当找到中间元素,跳出 。
class Solution {
public:
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
vector<int> record(nums1.size() + nums2.size(), 0);
int n = nums1.size();
int m = nums2.size();
int t = (n + m) / 2;
int t1 = (n + m) % 2;
int l = 0;
int r = 0;
int j = 0;
while(l + r <= t) {
if(l < n && r < m && nums1[l] >= nums2[r]) {
record[j++] = nums2[r];
r++;
}
else if(l < n && r < m && nums1[l] < nums2[r]){
record[j++] = nums1[l];
l++;
}
else if(l >= n && r < m ) {
record[j++] = nums2[r];
r++;
}
else if(l < n && r >= m ) {
record[j++] = nums1[l];
l++;
}
else break;
}
if(t1 == 1) {
return record[l + r - 1];
}
return double(record[r + l - 2] + record[r + l - 1]) / 2 ;
}
};
class Solution {
public:
int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
/* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
* 这里的 "/" 表示整除
* nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
* nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
* 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
* 这样 pivot 本身最大也只能是第 k-1 小的元素
* 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
* 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
* 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
*/
int m = nums1.size();
int n = nums2.size();
int index1 = 0, index2 = 0;
while (true) {
// 边界情况
if (index1 == m) {
return nums2[index2 + k - 1];
}
if (index2 == n) {
return nums1[index1 + k - 1];
}
if (k == 1) {
return min(nums1[index1], nums2[index2]);
}
// 正常情况
int newIndex1 = min(index1 + k / 2 - 1, m - 1);
int newIndex2 = min(index2 + k / 2 - 1, n - 1);
int pivot1 = nums1[newIndex1];
int pivot2 = nums2[newIndex2];
if (pivot1 <= pivot2) {
k -= newIndex1 - index1 + 1;
index1 = newIndex1 + 1;
}
else {
k -= newIndex2 - index2 + 1;
index2 = newIndex2 + 1;
}
}
}
double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
int totalLength = nums1.size() + nums2.size();
if (totalLength % 2 == 1) {
return getKthElement(nums1, nums2, (totalLength + 1) / 2);
}
else {
return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
}
}
};
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/median-of-two-sorted-arrays/solution/xun-zhao-liang-ge-you-xu-shu-zu-de-zhong-wei-s-114/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
3.2 正则化表达
10. 正则表达式匹配https://leetcode-cn.com/problems/regular-expression-matching/
给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。
难 题没读懂
示例 1:
输入:s = "aa", p = "a"
输出:false
解释:"a" 无法匹配 "aa" 整个字符串。
示例 2:
输入:s = "aa", p = "a*"
输出:true
解释:因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此,字符串 "aa" 可被视为 'a' 重复了一次。
示例 3:
输入:s = "ab", p = ".*"
输出:true
解释:".*" 表示可匹配零个或多个('*')任意字符('.')。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/regular-expression-matching
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这里是官方代码,解释请去官方
class Solution {
public:
bool isMatch(string s, string p) {
int m = s.size();
int n = p.size();
auto matches = [&](int i, int j) {
if (i == 0) {
return false;
}
if (p[j - 1] == '.') {
return true;
}
return s[i - 1] == p[j - 1];
};
vector<vector<int>> f(m + 1, vector<int>(n + 1));
f[0][0] = true;
for (int i = 0; i <= m; ++i) {
for (int j = 1; j <= n; ++j) {
if (p[j - 1] == '*') {
f[i][j] |= f[i][j - 2];
if (matches(i, j - 1)) {
f[i][j] |= f[i - 1][j];
}
}
else {
if (matches(i, j)) {
f[i][j] |= f[i - 1][j - 1];
}
}
}
}
return f[m][n];
}
};
3.3 合并K个升序链表
23. 合并K个升序链表https://leetcode-cn.com/problems/merge-k-sorted-lists/
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
class Solution {
public:
class mycompare {
public:
bool operator()(const ListNode* A, const ListNode *B) {
return A->val > B->val;
}
};
ListNode* mergeKLists(vector<ListNode*>& lists) {
//创建一个小顶堆
priority_queue<ListNode*, vector<ListNode*>, mycompare> que;
//将数据压入优先队列中
for(int i = 0; i < lists.size(); i++) {
auto t = lists[i];
while(t != NULL) {
que.push(t);
t = t->next;
}
}
//申明一个虚拟头结点
//这里一定要注意链表的结尾要赋予空
ListNode* head = new ListNode(0);
ListNode* cur = head;
while(!que.empty()) {
cur->next = que.top();
cur = cur->next;
cur->next = NULL;
que.pop();
}
return head->next;
}
};
3.4 最小栈
155. 最小栈https://leetcode-cn.com/problems/min-stack/
设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack 类:
MinStack() 初始化堆栈对象。
void push(int val) 将元素val推入堆栈。
void pop() 删除堆栈顶部的元素。
int top() 获取堆栈顶部的元素。
int getMin() 获取堆栈中的最小元素。
输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
class MinStack {
public:
stack<int> q;
stack<int> p;
MinStack() {
p.push(INT_MAX);
}
void push(int val) {
q.push(val);
if(val < p.top()) {
p.push(val);
}
else {
p.push(p.top());
}
}
void pop() {
q.pop();
p.pop();
}
int top() {
return q.top();
}
int getMin() {
return p.top();
}
};
class MinStack {
public:
stack<int> q;
stack<int> p;
MinStack() {
p.push(INT_MAX);
}
void push(int val) {
q.push(val);
if(val <= p.top()) {
p.push(val);
}
}
void pop() {
int t = q.top();
if(t == p.top()) {
p.pop();
}
q.pop();
}
int top() {
return q.top();
}
int getMin() {
return p.top();
}
};
3.5 回文链表
234. 回文链表https://leetcode-cn.com/problems/palindrome-linked-list/
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
进阶:你能否用 O(n)
时间复杂度和 O(1)
空间复杂度解决此题?
class Solution {
public:
bool isPalindrome(ListNode* head) {
int n = 0;
ListNode* cur = head;
while(cur) {
n++;
cur = cur->next;
}
cur = head;
ListNode* hummy = new ListNode(0);
int temp = n / 2;
while(temp > 0) {
cur = cur->next;
temp--;
}
ListNode* pre = NULL;
while(cur) {
ListNode* t = cur->next;
cur->next = pre;
pre = cur;
cur = t;
}
cur = head;
ListNode* op = pre;
bool t = true;
while(cur && pre) {
if(cur->val != pre->val) {
t = false;
break;
}
cur = cur->next;
pre = pre->next;
}
return t;
}
};
3.6 找到所有数组中消失的数字
448. 找到所有数组中消失的数字https://leetcode-cn.com/problems/find-all-numbers-disappeared-in-an-array/
给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。
示例 1:
输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]
示例 2:
输入:nums = [1,1]
输出:[2]
进阶:你能在不使用额外空间且时间复杂度为 O(n)
的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。
class Solution {
public:
vector<int> findDisappearedNumbers(vector<int>& nums) {
vector<int> result;
int n = nums.size();
for(int num : nums) {
int x = (num - 1) % n;
nums[x] = nums[x] + n;
}
for(int i = 0; i < nums.size(); i++) {
if(nums[i] <= n) {
result.push_back(i + 1);
}
}
return result;
}
};
3.7 二叉树的直径
543. 二叉树的直径https://leetcode-cn.com/problems/diameter-of-binary-tree/
给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
示例 :
给定二叉树
1
/ \
2 3
/ \
4 5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。
class Solution {
public:
int ans = 0;
int recur(TreeNode* root) {
if(root == NULL) {
return 0;
}
int l = recur(root->left);
int r = recur(root->right);
ans = max(ans, l + r);
return max(l, r) + 1;
}
int diameterOfBinaryTree(TreeNode* root) {
recur(root);
return ans;
}
};
3.8 下一个排列
31. 下一个排列https://leetcode-cn.com/problems/next-permutation/
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]
难,参考官方代码
class Solution {
public:
void nextPermutation(vector<int>& nums) {
int i = nums.size() - 2;
while(i >=0 && nums[i] >= nums[i + 1]) {
i--;
}
if(i >= 0) {
int j = nums.size() - 1;
for(; j > i; j--) {
if(nums[j] > nums[i]) {
swap(nums[j], nums[i]);
break;
}
}
}
reverse(nums.begin() + i + 1, nums.end());
}
};
3.9 旋转图像
48. 旋转图像https://leetcode-cn.com/problems/rotate-image/
给定一个 n × n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。
你必须在 原地 旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for(int i = 0; i < matrix.size() / 2; i++) {
for(int j = 0; j < (matrix[0].size() + 1) / 2; j++) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
};
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
vector<vector<int>> temp = matrix;
for(int i = 0; i < matrix.size() ; i++) {
for(int j = 0; j < matrix[0].size(); j++) {
matrix[i][j] = temp[matrix.size() - 1 - j][i];
}
}
3.10 字母异位词分组
49. 字母异位词分组https://leetcode-cn.com/problems/group-anagrams/
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的字母得到的一个新单词,所有源单词中的字母通常恰好只用一次。
示例 1:
输入: strs = ["eat", "tea", "tan", "ate", "nat", "bat"]
输出: [["bat"],["nat","tan"],["ate","eat","tea"]]
示例 2:
输入: strs = [""]
输出: [[""]]
示例 3:
输入: strs = ["a"]
输出: [["a"]]
暴力解法 超时
class Solution {
public:
bool comapreStr(string s1, string s2) {
if(s1.size() != s2.size()) return false;
int a[26] = {0};
for(int i = 0; i < s1.size(); i++) {
a[s1[i] - 'a']++;
}
for(int j = 0; j < s2.size(); j++) {
if(a[s2[j] - 'a'] == 0) return false;
a[s2[j] - 'a']--;
}
for(int i = 0; i < 0; i++) {
if(a[i] != 0) return false;
}
return true;
}
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> result;
vector<int> visited(strs.size(), 0);
for(int i = 0; i < strs.size(); i++) {
if(visited[i] == 0) {
visited[i] = 1;
vector<string> str;
str.push_back(strs[i]);
for(int j = 0; j < strs.size(); j++) {
if(visited[j] == 0 && comapreStr(strs[i], strs[j])) {
visited[j] = 1;
str.push_back(strs[j]);
}
}
if(str.size() >= 1) {
result.push_back(str);
}
}
}
return result;
}
};
class Solution {
public:
bool comapreStr(string s1, string s2) {
if(s1.size() != s2.size()) return false;
int a[26] = {0};
for(int i = 0; i < s1.size(); i++) {
a[s1[i] - 'a']++;
}
for(int j = 0; j < s2.size(); j++) {
if(a[s2[j] - 'a'] == 0) return false;
a[s2[j] - 'a']--;
}
for(int i = 0; i < 0; i++) {
if(a[i] != 0) return false;
}
return true;
}
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> result;
for(int i = 0; i < strs.size(); i++) {
vector<string> str;
if(strs[i] != "0") {
str.push_back(strs[i]);
for(int j = i + 1; j < strs.size(); j++) {
if(strs[j] != "0" && comapreStr(strs[i], strs[j])) {
str.push_back(strs[j]);
strs[j] = "0";
}
}
result.push_back(str);
}
}
return result;
}
};
官方代码 利用哈希表 对原数组进行排序,作为哈希表的键,排序的时间复杂度O(nlogn),遍历元素为n, 所以时间复杂度为O(n^2logn),上面的代码是O(n^3)
class Solution {
public:
unordered_map<string, vector<string>> map;
void getGroupAnagrams(vector<string>& strs) {
for(string str : strs) {
string key = str;
sort(key.begin(), key.end());
map[key].push_back(str);
}
}
vector<vector<string>> groupAnagrams(vector<string>& strs) {
vector<vector<string>> ans;
getGroupAnagrams(strs);
for(unordered_map<string, vector<string>>::iterator it = map.begin(); it != map.end(); it++) {
ans.push_back(it->second);
}
return ans;
}
};
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for(int i = 0; i < strs.size(); i++) {
string temp = strs[i];
sort(temp.begin(), temp.end());
mp[temp].push_back(strs[i]);
}
vector<vector<string>> result;
for(unordered_map<string, vector<string>>::iterator it = mp.begin(); it != mp.end(); it++) {
result.push_back(it->second);
}
return result;
}
};
3.11 合并区间
56. 合并区间https://leetcode-cn.com/problems/merge-intervals/
以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
区间题很容易弄混,大家都思考几遍
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。
class Solution {
public:
static bool cmp(vector<int>& a, vector<int>& b) {
if(a[0] == b[0]) {
return a[1] < b[1];
}
return a[0] < b[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> result;
sort(intervals.begin(), intervals.end(), cmp);
for(int i = 0; i < intervals.size(); i++) {
int l = intervals[i][0];
int r = intervals[i][1];
while(i < (intervals.size() - 1) && intervals[i + 1][0] <= r ) {
r = max(r, intervals[i + 1][1]);
i++;
}
if(result.size() > 0 && result[result.size() - 1][1] >= l) result[result.size() - 1][1] = max(r, intervals[i][1]);
else result.push_back({l,max(r, intervals[i][1])});
}
return result;
}
};
3.12 颜色分类
75. 颜色分类https://leetcode-cn.com/problems/sort-colors/
给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
必须在不使用库的sort函数的情况下解决这个问题。
同样的思路写出来的代码不一样 哎 能力还是不行
class Solution {
public:
void sortColors(vector<int>& nums) {
int l = 0;
int r = nums.size() - 1;
while(l < r) {
while(l <= r && nums[r] != 0) {
r--;
}
while(l <= r && nums[l] == 0) {
l++;
}
if(l < r)
swap(nums[l], nums[r]);
}
r = nums.size() - 1;
while(l < r) {
while(l <= r && nums[r] != 1) {
r--;
}
while(l <= r && nums[l] == 1) {
l++;
}
if(l < r)
swap(nums[l], nums[r]);
}
}
};
官方代码:
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int ptr = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] == 0) {
swap(nums[i], nums[ptr]);
++ptr;
}
}
for (int i = ptr; i < n; ++i) {
if (nums[i] == 1) {
swap(nums[i], nums[ptr]);
++ptr;
}
}
}
};
class Solution {
public:
void sortColors(vector<int>& nums) {
int n = nums.size();
int p0 = 0, p1 = 0;
for (int i = 0; i < n; ++i) {
if (nums[i] == 1) {
swap(nums[i], nums[p1]);
++p1;
} else if (nums[i] == 0) {
swap(nums[i], nums[p0]);
if (p0 < p1) {
swap(nums[i], nums[p1]);
}
++p0;
++p1;
}
}
}
};
3.13 最长连续序列
128. 最长连续序列https://leetcode-cn.com/problems/longest-consecutive-sequence/
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入:nums = [100,4,200,1,3,2]
输出:4
解释:最长数字连续序列是 [1, 2, 3, 4]。它的长度为 4。
示例 2:
输入:nums = [0,3,7,2,5,8,4,6,0,1]
输出:9
作者实现代码先排序在哈希表 在动态规划 时间复杂度:O(nlogn)
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
if(nums.size() == 0) return 0;
vector<int> dp(nums.size(), 1);
sort(nums.begin(), nums.end());
dp[0] = 1;
int mmax = 1;
for(int i = 1; i < nums.size(); i++) {
if(nums[i] == nums[i - 1] + 1) {
dp[i] = dp[i - 1] + 1;
}
else if(nums[i] == nums[i - 1]) {
dp[i] = dp[i - 1];
}
mmax = max(dp[i], mmax);
}
return mmax;
}
};
官方代码 利用哈希表 将重复元素和判断数组是否为空都省略了 思路真好
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> record;
//真好,把相同重复的元素也排除在外了
for(int num : nums) {
record.insert(num);
}
int longnum = 0;
for(int num : record) {
if(!record.count(num - 1)) {
int curnum = num + 1;
int curlong = 1;
while(record.count(curnum)) {
curlong++;
curnum += 1;
}
longnum = max(longnum, curlong);
}
}
return longnum;
}
};
3.14 二叉树展开为链表
114. 二叉树展开为链表https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/
给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。
输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [0]
输出:[0]
class Solution {
public:
void flatten(TreeNode* root) {
stack<TreeNode*> st;
if(root == NULL) return;
st.push(root);
vector<TreeNode*> l;
while(!st.empty()) {
auto t = st.top();
st.pop();
l.push_back(t);
if(t->right != NULL) {
st.push(t->right);
}
if(t->left != NULL) {
st.push(t->left);
}
}
for(int i = 1; i < l.size(); i++) {
TreeNode* pre =l[i];
TreeNode* cur = l[i - 1];
cur->left = NULL;
cur->right = pre;
}
}
};
第4天
4.1 337. 打家劫舍 III
337. 打家劫舍 IIIhttps://leetcode-cn.com/problems/house-robber-iii/
小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果 两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。
给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
class Solution {
public:
unordered_map<TreeNode *, int> f, g;
void dfs(TreeNode* root) {
if(root == NULL) return;
dfs(root->left);
dfs(root->right);
f[root] = root->val + g[root->left] + g[root->right];
g[root] = max(f[root->left], g[root->left]) + max(f[root->right], g[root->right]);
return ;
}
int rob(TreeNode* root) {
if(root == NULL) return 0;
dfs(root);
return max(f[root], g[root]);
}
};
4.2 309. 最佳买卖股票时机含冷冻期
309. 最佳买卖股票时机含冷冻期https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/
给定一个整数数组prices,其中第 prices[i] 表示第 i 天的股票价格 。
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: prices = [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]
示例 2:
输入: prices = [1]
输出: 0
class Solution {
public:
int maxProfit(vector<int>& prices) {
vector<vector<int>> dp(prices.size(), vector(4, 0));
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = 0;
dp[0][3] = 0;
for(int i = 1; i < prices.size(); i++) {
dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][2], dp[i - 1][3]) - prices[i]);
dp[i][1] = dp[i - 1][0] + prices[i];
dp[i][2] = dp[i - 1][1];
dp[i][3] = max(dp[i - 1][2], dp[i - 1][3]);
}
int n = prices.size();
return max(dp[n - 1][1], max(dp[n - 1][3], dp[n - 1][2]));
}
};
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
vector<vector<int>> dp(n, vector(3,0));
dp[0][0] = -prices[0];
dp[0][1] = 0;
dp[0][2] = 0;
for(int i = 1; i < n; i++) {
dp[i][0] = max(dp[i - 1][0], dp[i - 1][2] - prices[i]);
dp[i][1] = dp[i - 1][0] + prices[i];
dp[i][2] = max(dp[i - 1][2], dp[i - 1][1]);
}
return max(dp[n - 1][1], dp[n - 1][2]);
}
};
4.3 148. 排序链表
148. 排序链表https://leetcode-cn.com/problems/sort-list/
给你链表的头结点 head
,请将其按 升序 排列并返回 排序后的链表 。
时间复杂度O(n),时间复杂度O(n)
输入:head = [4,2,1,3]
输出:[1,2,3,4]
class Solution {
public:
class cmp {
public:
bool operator()(const pair<ListNode*, int>& a, const pair<ListNode*, int>& b) {
return a.second > b.second;
}
};
ListNode* sortList(ListNode* head) {
if(head == NULL) return head;
priority_queue<pair<ListNode*, int>, vector<pair<ListNode*, int>>, cmp> que;
ListNode *cur = head;
while(cur) {
que.push({cur,cur->val});
cur = cur->next;
}
ListNode *myhead = new ListNode(0);
cur = myhead;
while(!que.empty()) {
auto t = que.top();
que.pop();
cur->next = t.first;
cur = cur->next;
}
cur->next = NULL;
return myhead->next;
}
};
官方使用的归并排序 对于归并排序不了解 等了解了再来写这道题的代码。
归并排序需要最后合并两个有序链表,可以先做一下这道题
4.4 21. 合并两个有序链表
21. 合并两个有序链表https://leetcode-cn.com/problems/merge-two-sorted-lists/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* cur1 = list1;
ListNode* cur2 = list2;
ListNode* hummy = new ListNode(0);
ListNode* cur = hummy;
while(cur1 || cur2) {
if(!cur1 && cur2) {
cur->next = cur2;
break;
}
else if(!cur2 && cur1) {
cur->next = cur1;
break;
}
else if(cur1->val > cur2->val) {
cur->next = cur2;
cur = cur->next;
cur2 = cur2->next;
}
else {
cur->next = cur1;
cur = cur->next;
cur1 = cur1->next;
}
}
return hummy->next;
}
};
官方的复杂度为:O(nlgn)时间复杂度为O(1)
直接在链表的基础上进行操作
有兴趣也可以看看这道题,对链表进行排序
4.5 147. 对链表进行插入排序
147. 对链表进行插入排序 - 力扣(LeetCode) (leetcode-cn.com)https://leetcode-cn.com/problems/insertion-sort-list/
对于147的答案:
class Solution {
public:
ListNode* insertionSortList(ListNode* head) {
if(!head) return head;
ListNode* hummy = new ListNode(0);
hummy->next = head;
ListNode* lastsort = head;
ListNode* cur = head->next;
while(cur) {
if(lastsort->val <= cur->val) {
lastsort = lastsort->next;
}
else {
ListNode* pre = hummy;
while(pre->next->val < cur->val ) {
pre = pre->next;
}
lastsort->next = cur->next;
ListNode* temp = pre->next;
pre->next = cur;
cur->next = temp;
}
cur = lastsort->next;
}
return hummy->next;
}
};
对于改题,使用插入排序:
class Solution {
public:
ListNode* sortList(ListNode* head) {
ListNode* hummy = new ListNode(0);
hummy->next = head;
ListNode* cur = head;
ListNode* pre = NULL;
while(cur) {
if(pre == NULL) {
pre = cur;
cur = cur->next;
}
else {
if(pre->val > cur->val) {
ListNode* mhead = new ListNode(0);
mhead = hummy;
while(mhead->next->val < cur->val) {
mhead = mhead->next;
}
ListNode* temp = cur->next;
ListNode* temp1 = mhead->next;
mhead->next = cur;
pre->next = temp;
cur->next = temp1;
cur = temp;
}
else {
pre = cur;
cur = cur->next;
}
}
}
return hummy->next;
}
};
这是作者写的归并排序:
class Solution {
public:
ListNode* mergeSort(ListNode* head) {
if(!head || !head->next) {
return head;
}
ListNode* slow = head;
ListNode* faster = head;
while(faster && faster->next && faster->next->next) {
slow = slow->next;
faster = faster->next->next;
}
cout << slow->val << endl;
ListNode* temp = slow->next;
slow->next = NULL;
ListNode* head1 = mergeSort(head);
ListNode* head2 = mergeSort(temp);
ListNode* mhead2 = new ListNode(0);
ListNode* next = mhead2;
while(head1 || head2) {
if(!head1 && head2) {
next->next = head2;
break;
}
else if(!head2 && head1) {
next->next = head1;
break;
}
else if(head1->val < head2->val) {
next->next = head1;
head1 = head1->next;
next = next->next;
}
else if(head1->val >= head2->val){
next->next = head2;
head2 = head2->next;
next = next->next;
}
}
return mhead2->next;
}
ListNode* sortList(ListNode* head) {
return mergeSort(head);
}
};
class Solution {
public:
void mergeTwoList(ListNode* l1, ListNode* l2, ListNode* cur) {
if(l1 && !l2) {
cur->next = l1;
return;
}
if(!l1 && l2) {
cur->next = l2;
return;
}
if(l1->val > l2->val) {
cur->next = l2;
cur = cur->next;
mergeTwoList(l1, l2->next, cur);
}
else {
cur->next = l1;
cur = cur->next;
mergeTwoList(l1->next, l2, cur);
}
}
ListNode* mergeSort(ListNode* head) {
if(!head || !head->next) {
return head;
}
ListNode* slow = head;
ListNode* faster = head;
while(faster && faster->next && faster->next->next) {
slow = slow->next;
faster = faster->next->next;
}
ListNode* temp = slow->next;
slow->next = NULL;
ListNode* head1 = mergeSort(head);
ListNode* head2 = mergeSort(temp);
ListNode* mhead = new ListNode(0);
ListNode* cur = mhead;
mergeTwoList(head1, head2, cur);
return mhead->next;
}
ListNode* sortList(ListNode* head) {
return mergeSort(head);
}
};
官方空间复杂度为O(1):
class Solution {
public ListNode sortList(ListNode head) {
if (head == null) {
return head;
}
int length = 0;
ListNode node = head;
while (node != null) {
length++;
node = node.next;
}
ListNode dummyHead = new ListNode(0, head);
for (int subLength = 1; subLength < length; subLength <<= 1) {
ListNode prev = dummyHead, curr = dummyHead.next;
while (curr != null) {
ListNode head1 = curr;
for (int i = 1; i < subLength && curr.next != null; i++) {
curr = curr.next;
}
ListNode head2 = curr.next;
curr.next = null;
curr = head2;
for (int i = 1; i < subLength && curr != null && curr.next != null; i++) {
curr = curr.next;
}
ListNode next = null;
if (curr != null) {
next = curr.next;
curr.next = null;
}
ListNode merged = merge(head1, head2);
prev.next = merged;
while (prev.next != null) {
prev = prev.next;
}
curr = next;
}
}
return dummyHead.next;
}
public ListNode merge(ListNode head1, ListNode head2) {
ListNode dummyHead = new ListNode(0);
ListNode temp = dummyHead, temp1 = head1, temp2 = head2;
while (temp1 != null && temp2 != null) {
if (temp1.val <= temp2.val) {
temp.next = temp1;
temp1 = temp1.next;
} else {
temp.next = temp2;
temp2 = temp2.next;
}
temp = temp.next;
}
if (temp1 != null) {
temp.next = temp1;
} else if (temp2 != null) {
temp.next = temp2;
}
return dummyHead.next;
}
}
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/sort-list/solution/pai-xu-lian-biao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
4.6 152. 乘积最大子数组
152. 乘积最大子数组https://leetcode-cn.com/problems/maximum-product-subarray/
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
class Solution {
public:
int maxProduct(vector<int>& nums) {
vector<int> dpmax(nums.size(), 1);
vector<int> dpmin(nums.size(), 1);
dpmax[0] = nums[0];
dpmin[0] = nums[0];
int mmax = nums[0];
for(int i = 1; i < nums.size(); i++) {
dpmax[i] = max(max(nums[i], dpmax[i - 1] * nums[i]), dpmin[i - 1] * nums[i]);
dpmin[i] = min(min(nums[i], dpmax[i - 1] * nums[i]), dpmin[i - 1] * nums[i]);
mmax = max(mmax, dpmax[i]);
}
return mmax;
}
};
第5天 (拓扑排序)
5.1 207. 课程表
207. 课程表https://leetcode-cn.com/problems/course-schedule/
你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false 。
示例 1:
输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:
输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
这道题 我首先思想是对其就是优先队列排队,内容和第四天的排序链表那道题类似,然后找区间是否有交区,如果有交区,那么就不行。只是这道题不行, 比如说[0,1],[0,2], 或者[1,0],[2,1],[0,2]。然后又想到了哈希表。哈哈 看答案才知道是拓扑排序 和上面题 归并排序一样 不是很懂 所以需要区学习 这道题先放到 等学完了 再来实现代码。
5.2 215. 数组中的第K个最大元素
215. 数组中的第K个最大元素https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
给定整数数组 nums
和整数 k
,请返回数组中第 k
个最大的元素。
请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
这道图 最简单的方法就是对其就行排序,然后直接知道第k大的元素
时间复杂度:O(nlongn)空间复杂度O(1)
还可以用优先队列,时间复杂度O(n),空间复杂度为O(n)空间换时间
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int, vector<int>, less<int>> que;
for(int num:nums) {
que.push(num);
}
for(int j = k - 1; j > 0; j--) {
que.pop();
}
return que.top();
}
};
但是官方中等题肯定不是考你这个。
在思考哈
这道题要找到第、k大的数,那么肯定需要遍历整个数组,时间复杂度至少为O(n),那么思考的方向是怎么才能把空间复杂度降下去,如果要把空间复杂度降下去,那么就只能在原数组进行操作。
怎么对其进行操作了?排序?
好嘛 实在想不到更加好的算法了 ,那么就排序吧
这里选择归并排序 试试
class Solution {
public:
void maxHeapify(vector<int>& nums, vector<int>& temp, int l, int r) {
if(l >= r) return;
int mid = l + (r - l) / 2;
maxHeapify(nums, temp, l, mid);
maxHeapify(nums, temp, mid + 1, r);
if(nums[mid] < nums[mid + 1]) return;
mtemp(nums, temp, l, r);
}
void mtemp(vector<int>& nums, vector<int>& temp, int l, int r) {
int mid = l + (r - l) / 2;
for(int i = l; i <= r; i++) {
temp[i] = nums[i];
}
int i = l;
int j = mid + 1;
for(int k = l; k <= r; k++) {
if(i == mid + 1) {
nums[k] = temp[j];
j++;
}
else if(j == r + 1) {
nums[k] = temp[i];
i++;
}
else if(temp[i] > temp[j]) {
nums[k] = temp[j];
j++;
}
else {
nums[k] = temp[i];
i++;
}
}
}
int findKthLargest(vector<int>& nums, int k) {
vector<int> temp(nums);
maxHeapify(nums, temp, 0, nums.size() - 1);
int j = nums.size() - 1;
for(; j > nums.size() - k; j--);
return nums[j];
}
};
也能过 但是时间复杂度为O(nlongn),空间复杂度为O(n);
这里推荐一个对于归并排序的经典使用:
剑指 Offer 51. 数组中的逆序对https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
使用快排:在力扣上运行很久 很奇怪 差不多是之前的10倍
class Solution {
public:
void quickSearch(vector<int>& nums,int l, int r) {
if( l >= r) return;
int temp = nums[l];
int i = l;
int j = r;
while(i < j) {
while(i < j && nums[j] > temp) {
j--;
}
if(i < j) {
nums[i] = nums[j];
i++;
}
while(i < j && nums[i] < temp) {
i++;
}
if(i < j) {
nums[j] = nums[i];
j--;
}
}
nums[j] = temp;
quickSearch(nums, l, i - 1);
quickSearch(nums, i + 1, r);
}
int findKthLargest(vector<int>& nums, int k) {
vector<int> temp(nums);
quickSearch(nums, 0, nums.size() - 1);
int j = nums.size() - 1;
for(; j > nums.size() - k; j--);
return nums[j];
}
};
快排的改进
class Solution {
public:
int quickSelect(vector<int>& nums, int k, int i, int j) {
if(i >= j) return nums[i];
int l = i;
int r = j;
int temp = nums[l];
while(l < r) {
while(l <r && nums[r] > temp) {
r--;
}
if(l < r) {
nums[l] = nums[r];
l++;
}
while(l < r && nums[l] < temp) {
l++;
}
if(l < r) {
nums[r] = nums[l];
r--;
}
}
nums[l] = temp;
if(l == k) return nums[l];
return (k < l)?quickSelect(nums, k, i, l - 1):quickSelect(nums, k, l + 1, j);
}
int findKthLargest(vector<int>& nums, int k) {
return quickSelect(nums, nums.size() - k, 0, nums.size() - 1);
}
};
堆排序
class Solution {
public:
//最大堆的建立
void max_head(vector<int>& nums, int dad, int end) {
int son = dad * 2 + 1;
while(son <= end) {
if(son + 1 <= end && nums[son] < nums[son + 1]) {
son++;
}
if(nums[dad] > nums[son]) {
return;
}
else {
swap(nums[dad], nums[son]);
dad = son;
son = 2 * dad + 1;
}
}
}
int headerSort(vector<int>& nums, int k) {
int len = nums.size();
for(int i = len / 2 - 1; i >= 0; i--) {
max_head(nums, i, len - 1);
}
int i = 0;
for(; i < len; i++) {
swap(nums[0], nums[len - 1 - i]);
k--;
if(k == 0) {
break;
}
max_head(nums, 0, len - 2 - i);
}
return nums[len - i - 1];
}
int findKthLargest(vector<int>& nums, int k) {
return headerSort(nums, k);
}
};
5.3 最多可以参加的会议数目
第6天
6.1 394. 字符串解码
394. 字符串解码https://leetcode-cn.com/problems/decode-string/
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
示例 1:
输入:s = "3[a]2[bc]"
输出:"aaabcbc"
示例 2:
输入:s = "3[a2[c]]"
输出:"accaccacc"
示例 3:
输入:s = "2[abc]3[cd]ef"
输出:"abcabccdcdcdef"
示例 4:
输入:s = "abc3[cd]xyz"
输出:"abccdcdcdxyz"
此题,一看到括号,就想到了栈,但是没写出来。主要是还有在括号外面的字母,比如说:
abc3[cd]xyz
本人作者思路是,依次入栈,然后遇到‘】’出栈,将栈付给字符串s,然后遇到数字,说明到了倍数,停止出栈,将字符串乘以数字。
class Solution {
public:
string decodeString(string s) {
stack<char> st;
if(s.size() == 0) return s;
string mys;
for(int i = 0; i < s.size(); i++) {
if(s[i] != ']') {
st.push(s[i]);
}
else if(s[i] == ']') {
string ms;
while(!st.empty() && st.top() < '0' || st.top() > '9') {
ms += st.top();
st.pop();
}
int num = 0;
int t = 1;
while(!st.empty() && st.top() >= '0' && st.top() <= '9'){
num += (st.top() - '0') * t;
t = t * 10;
st.pop();
}
int l = mys.size();
num--;
while(num--) {
mys += ms;
}
}
}
return mys;
}
};
官方思路也是栈,
class Solution {
public:
string decodeString(string s) {
stack<pair<int, string>> st;
int mul = 0;
string mys;
for(int i = 0; i < s.size(); i++) {
if(s[i] <= '9' && s[i] >= '0') {
mul = mul * 10 + (s[i] - '0');
}
else if(s[i] >= 'a' && s[i] <= 'z' ) {
mys += s[i];
}
else if(s[i] =='[') {
st.push({mul, mys});
mul = 0;
mys.clear();
}
else if(s[i] ==']') {
auto [last_mul, last_mys] = st.top();
st.pop();
int n = mys.size();
last_mul--;
while(last_mul--) {
mys += mys.substr(0, n);
}
mys = last_mys + mys;
}
}
return mys;
}
};
6.2 逆波兰表达式求值
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
注意 两个整数之间的除法只保留整数部分。
可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
class Solution {
public:
int evalRPN(vector<string>& tokens) {
if(tokens.size() == 1) {
return atoi(tokens[0].c_str());
}
stack<string> st;
vector<string> mys(tokens);
int t;
for(int i = 0; i < mys.size(); i++) {
if(mys[i] == "+") {
int t1 = atoi(st.top().c_str());
st.pop();
int t2 = atoi(st.top().c_str());
st.pop();
t = t1 + t2;
st.push(to_string(t));
}
else if(mys[i] == "-") {
int t1 = atoi(st.top().c_str());
st.pop();
int t2 = atoi(st.top().c_str());
st.pop();
t = t2 - t1;
st.push(to_string(t));
}
else if(mys[i] == "*") {
int t1 = atoi(st.top().c_str());
st.pop();
int t2 = atoi(st.top().c_str());
st.pop();
t = t2 * t1;
st.push(to_string(t));
}
else if(mys[i] == "/") {
int t1 = atoi(st.top().c_str());
st.pop();
int t2 = atoi(st.top().c_str());
st.pop();
t = t2 / t1;
st.push(to_string(t));
}
else st.push(mys[i]);
}
return t;
}
};
第7天 手撕双向链表+哈希表
7.1 LRU缓存
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache 类:
LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值 value ;如果不存在,则向缓存中插入该组 key-value 。如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字。
函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。
输入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
输出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解释
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
struct DLinkedNode {
int key, value;
DLinkedNode* pre;
DLinkedNode* next;
DLinkedNode():key(0), value(0), pre(nullptr), next(nullptr) {}
DLinkedNode(int _key, int _value): key(_key), value(_value), pre(nullptr), next(nullptr) {} ;
};
class LRUCache {
public:
unordered_map<int, DLinkedNode*> cache;
DLinkedNode* head;
DLinkedNode* tail;
int size;
int capacity;
LRUCache(int capacity) {
head = new DLinkedNode();
tail = new DLinkedNode();
head->next = tail;
tail->pre = head;
this->capacity = capacity;
}
int get(int key) {
if (!cache.count(key)) {
return -1;
}
DLinkedNode* node = cache[key];
moveToHead(node);
return node->value;
}
void removeNode(DLinkedNode* node) {
node->pre->next = node->next;
node->next->pre = node->pre;
}
void moveToHead(DLinkedNode* node) {
removeNode(node);
node->pre = head;
node->next = head->next;
head->next->pre = node;
head->next = node;
}
void addTohead(DLinkedNode* node) {
node->pre = head;
node->next = head->next;
head->next->pre = node;
head->next = node;
}
void put(int key, int value) {
if(cache.count(key)) {
cache[key]->value = value;
moveToHead(cache[key]);
}
else {
if(cache.size() == capacity) {
DLinkedNode* removed = removeTail();
cache.erase(removed->key);
delete removed;
}
DLinkedNode* node = new DLinkedNode(key, value);
cache[key] = node;
addTohead(node);
}
}
DLinkedNode* removeTail(){
DLinkedNode* node = tail->pre;
removeNode(node);
return node;
}
};
7.2 最大正方形
221. 最大正方形https://leetcode-cn.com/problems/maximal-square/
在一个由 '0'
和 '1'
组成的二维矩阵内,找到只包含 '1'
的最大正方形,并返回其面积。
输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:4
class Solution {
public:
int maximalSquare(vector<vector<char>>& matrix) {
int n = matrix.size();
int m = matrix[0].size();
int result = 0;
vector<vector<int>> dp(n, vector(m, 0));
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(matrix[i][j] == '1') {
if(i == 0 || j == 0) {
dp[i][j] = 1;
}
else {
dp[i][j] = min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1])) + 1;
}
}
result = max(result, dp[i][j]);
}
}
return result * result;
}
};
第8天 正向逆向结题
8.1 除自身以外数组的乘积
238. 除自身以外数组的乘积https://leetcode-cn.com/problems/product-of-array-except-self/
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。
题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。
请不要使用除法,且在 O(n) 时间复杂度内完成此题。
示例 1:
输入: nums = [1,2,3,4]
输出: [24,12,8,6]
示例 2:
输入: nums = [-1,1,0,-3,3]
输出: [0,0,9,0,0]
时间复杂度:O(n) 空间复杂度O(n)
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size() ;
vector<int> preNum(n, 1);
vector<int> bankNum(n, 1);
vector<int> answare(n, 1);
for(int i = 1; i < n; i++) {
preNum[i] = preNum[i - 1] * nums[i - 1];
}
for(int j = n - 2; j >=0; j--) {
bankNum[j] = bankNum[j + 1] * nums[j + 1];
}
for(int i = 0; i < n; i++) {
answare[i] = preNum[i] * bankNum[i];
}
return answare;
}
};
时间复杂度:O(n) 空间复杂度O(1)
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size() ;
vector<int> bankNum(n, 1);
vector<int> answare(n, 1);
for(int i = 1; i < n; i++) {
answare[i] = answare[i - 1] * nums[i - 1];
}
int t = 1;
for(int j = n - 2; j >= 0; j--) {
t *= nums[j + 1];
answare[j] = answare[j] * t;
}
return answare;
}
};
8.2 乘积最大子数组
152. 乘积最大子数组https://leetcode-cn.com/problems/maximum-product-subarray/
给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。
测试用例的答案是一个 32-位 整数。
子数组 是数组的连续子序列。
示例 1:
输入: nums = [2,3,-2,4]
输出: 6
解释: 子数组 [2,3] 有最大乘积 6。
示例 2:
输入: nums = [-2,0,-1]
输出: 0
解释: 结果不能为 2, 因为 [-2,-1] 不是子数组。
class Solution {
public:
int maxProduct(vector<int>& nums) {
vector<int> dpmax(nums.size(), 1);
vector<int> dpmin(nums.size(), 1);
dpmax[0] = nums[0];
dpmin[0] = nums[0];
int mmax = nums[0];
for(int i = 1; i < nums.size(); i++) {
dpmax[i] = max(max(nums[i], dpmax[i - 1] * nums[i]), dpmin[i - 1] * nums[i]);
dpmin[i] = min(min(nums[i], dpmax[i - 1] * nums[i]), dpmin[i - 1] * nums[i]);
mmax = max(mmax, dpmax[i]);
}
return mmax;
}
};
class Solution {
public:
int maxProduct(vector<int>& nums) {
vector<int> mnums(nums);
reverse(mnums.begin(), mnums.end());
int max1 = nums[0];
int max2 = mnums[0];
int t1 = 1;
int t2 = 1;
for(int i = 0; i < nums.size(); i++) {
if(nums[i] != 0 && mnums[i] != 0 ) {
t1 *= nums[i];
t2 *= mnums[i];
max1 = max(max1, t1);
max2 = max(max2, t2);
}
else if(nums[i] == 0 && mnums[i] != 0 ){
t1 = 1;
t2 *= mnums[i];
max1 = max(max1, 0);
max2 = max(max2, t2);
}
else if(nums[i] != 0 && mnums[i] == 0 ){
t1 *= nums[i];
t2 = 1;
max1 = max(max1, t1);
max2 = max(max2, 0);
}
else {
t1 = 1;
t2 = 1;
max1 = max(max1, 0);
max2 = max(max2, 0);
}
}
return max(max1, max2);
}
};
8.3 搜索二维矩阵||
240. 搜索二维矩阵 IIhttps://leetcode-cn.com/problems/search-a-2d-matrix-ii/
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int n = 0;
int m = matrix[0].size() - 1;
while(n < matrix.size() && m>= 0) {
if(matrix[n][m] == target) {
return true;
}
else if(matrix[n][m] < target) {
n += 1;
}
else {
m -= 1;
}
}
return false;
}
};
8.4 寻找重复数
287. 寻找重复数https://leetcode-cn.com/problems/find-the-duplicate-number/
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
示例 1:
输入:nums = [1,3,4,2,2]
输出:2
示例 2:
输入:nums = [3,1,3,4,2]
输出:3
class Solution {
public:
int findDuplicate(vector<int>& nums) {
int n = nums.size();
for(int i = 0; i < nums.size(); i++) {
int temp = (nums[i] - 1) % nums.size();
nums[temp] += n;
}
for(int i = 0; i < nums.size(); i++) {
if(nums[i] > 2 * n) {
return i + 1;
}
}
return 0;
}
};
此题思路也可解题:
8.5 数组中重复的数字
剑指 Offer 03. 数组中重复的数字https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/
class Solution {
public:
//时间复杂度为O(n),空间复杂度为O(1)
int findRepeatNumber(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++) {
nums[i] = nums[i] + 1;
}
int n = nums.size();
for(int i = 0; i < nums.size(); i++) {
int temp = (nums[i] - 1) % n;
if(nums[temp] >= n) return (nums[i] - 1) % n;
nums[temp] += n;
}
return 0;
}
8.6 消失的数字
面试题 17.04. 消失的数字https://leetcode-cn.com/problems/missing-number-lcci/
数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
class Solution {
public:
int missingNumber(vector<int>& nums) {
if(nums.size() == 1 && nums[0] == 1) return 0;
nums.push_back(nums.size());
for(int i = 0; i < nums.size(); i++) {
nums[i] += 1;
}
int n = nums.size();
for(int i = 0; i < nums.size(); i++) {
int temp = (nums[i] - 1) % n;
nums[temp] += n;
}
for(int i = 0; i < nums.size() ; i++) {
if(nums[i] <= n ) return i;
}
return n - 1;
}
};
使用异或
class Solution {
public:
int missingNumber(vector<int>& nums) {
int n = nums.size();
int ans = 0;
for(int i = 1; i <= n; ++i){
ans ^= i; // [0, n]异或结果
ans ^= nums[i - 1]; // [0, n]异或结果与nums异或结果
}
return ans;
}
};
第9天 并查集
9.1 等式方程的可满足性
990. 等式方程的可满足性https://leetcode-cn.com/problems/satisfiability-of-equality-equations/
给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。
只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。
示例 1:
输入:["a==b","b!=a"]
输出:false
解释:如果我们指定,a = 1 且 b = 1,那么可以满足第一个方程,但无法满足第二个方程。没有办法分配变量同时满足这两个方程。
示例 2:输入:["b==a","a==b"]
输出:true
解释:我们可以指定 a = 1 且 b = 1 以满足满足这两个方程。
示例 3:输入:["a==b","b==c","a==c"]
输出:true
示例 4:输入:["a==b","b!=c","c==a"]
输出:false
示例 5:输入:["c==c","b==d","x!=z"]
输出:true来源:力扣(LeetCode)
class UnionFind { private: vector<int> parent; public: UnionFind() { parent.resize(26); for(int i = 0; i < 26; i++) { parent[i] = i; } } int find(int index) { if(parent[index] == index) { return index; } parent[index] = find(parent[index]); return parent[index]; } void unite(int index1, int index2) { parent[find(index1)] = find(index2); } }; class Solution { public: bool equationsPossible(vector<string>& equations) { UnionFind uf; for (const string& str: equations) { if (str[1] == '=') { int index1 = str[0] - 'a'; int index2 = str[3] - 'a'; uf.unite(index1, index2); } } for (const string& str: equations) { if (str[1] == '!') { int index1 = str[0] - 'a'; int index2 = str[3] - 'a'; if (uf.find(index1) == uf.find(index2)) { return false; } } } return true; } };
399. 除法求值https://leetcode-cn.com/problems/evaluate-division/
给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] = [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi = values[i] 。每个 Ai 或 Bi 是一个表示单个变量的字符串。
另有一些以数组 queries 表示的问题,其中 queries[j] = [Cj, Dj] 表示第 j 个问题,请你根据已知条件找出 Cj / Dj = ? 的结果作为答案。
返回 所有问题的答案 。如果存在某个无法确定的答案,则用 -1.0 替代这个答案。如果问题中出现了给定的已知条件中没有出现的字符串,也需要用 -1.0 替代这个答案。
注意:输入总是有效的。你可以假设除法运算中不会出现除数为 0 的情况,且不存在任何矛盾的结果。
有点复杂 先放到
560. 和为 K 的子数组https://leetcode-cn.com/problems/subarray-sum-equals-k/
class Solution { public: int subarraySum(vector<int>& nums, int k) { int count = 0; unordered_map<int, int> record; record[0] = 1;; int ans = 0; for (int i = 0; i < nums.size(); i++) { ans += nums[i]; if(record.find(ans - k) != record.end()) { count += record[ans - k]; } record[ans]++; } return count; } };
581. 最短无序连续子数组https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray/
给你一个整数数组 nums ,你需要找出一个 连续子数组 ,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
请你找出符合题意的 最短 子数组,并输出它的长度。
示例 1:
输入:nums = [2,6,4,8,10,9,15]
输出:5
解释:你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
示例 2:
输入:nums = [1,2,3,4]
输出:0
示例 3:
输入:nums = [1]
输出:0
class Solution {
public:
int findUnsortedSubarray(vector<int>& nums) {
vector<int> mynums(nums);
sort(mynums.begin(), mynums.end());
int l = 0;
int r = nums.size() - 1;
while(l <= r && nums[l] == mynums[l]) {
l++;
}
while( r >= l && nums[r] == mynums[r]) {
r--;
}
return r - l + 1;
}
};
第10天
10.1 重构字符串
767. 重构字符串https://leetcode-cn.com/problems/reorganize-string/
给定一个字符串 s
,检查是否能重新排布其中的字母,使得两相邻的字符不同。
返回 s
的任意可能的重新排列。若不可行,返回空字符串 ""
。
示例 1:
输入: s = "aab"
输出: "aba"
示例 2:
输入: s = "aaab"
输出: ""
class Solution {
public:
string reorganizeString(string s) {
if(s.size() < 2) {
return s;
}
int n = s.size();
int count[26] = {0};
int maxNum = 0;
for(char ch : s) {
count[ch - 'a']++;
maxNum = max(maxNum, count[ch - 'a']);
if(maxNum > (n + 1) / 2) return "";
}
string mys(n, ' ');
int enNum = 0;
int oddNum = 1;
int halfLength = n / 2;
for(int i = 0; i < 26; i++) {
if(count[i] == 0) {
continue;
}
while(count[i] > 0 && count[i] <= halfLength && oddNum < n) {
mys[oddNum] = i + 'a';
oddNum += 2;
count[i]--;
}
while(count[i] > 0) {
mys[enNum] = i + 'a';
enNum += 2;
count[i]--;
}
}
return mys;
}
};
10.2 任务调度器
621. 任务调度器https://leetcode-cn.com/problems/task-scheduler/
给你一个用字符数组 tasks 表示的 CPU 需要执行的任务列表。其中每个字母表示一种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。在任何一个单位时间,CPU 可以完成一个任务,或者处于待命状态。
然而,两个 相同种类 的任务之间必须有长度为整数 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。
你需要计算完成所有任务所需要的 最短时间 。
示例 1:
输入:tasks = ["A","A","A","B","B","B"], n = 2
输出:8
解释:A -> B -> (待命) -> A -> B -> (待命) -> A -> B
在本示例中,两个相同类型任务之间必须间隔长度为 n = 2 的冷却时间,而执行一个任务只需要一个单位时间,所以中间出现了(待命)状态。
示例 2:
输入:tasks = ["A","A","A","B","B","B"], n = 0
输出:6
解释:在这种情况下,任何大小为 6 的排列都可以满足要求,因为 n = 0
["A","A","A","B","B","B"]
["A","B","A","B","A","B"]
["B","B","B","A","A","A"]
...
诸如此类
示例 3:
输入:tasks = ["A","A","A","A","A","A","B","C","D","E","F","G"], n = 2
输出:16
解释:一种可能的解决方案是:
A -> B -> C -> A -> D -> E -> A -> F -> G -> A -> (待命) -> (待命) -> A -> (待命) -> (待命) -> A
class Solution {
public:
int leastInterval(vector<char>& tasks, int n) {
int t = tasks.size();
int record[26] = {0};
for(int i = 0; i < tasks.size(); i++) {
record[tasks[i] - 'A']++;
}
int mMax = 0;
for(int i = 0; i < 26; i++) {
mMax = max(mMax, record[i]);
}
int count = (mMax - 1) * (n + 1);
for(int i = 0; i < 26; i++) {
if(record[i] == mMax) {
count++;
}
}
return max(count, t);
}
};
题没读懂
10.3 路径之和
437. 路径总和 IIIhttps://leetcode-cn.com/problems/path-sum-iii/
给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。
示例 2:
输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:3
class Solution {
public:
int rootSum(TreeNode* root, int targetSum) {
if(!root) {
return 0;
}
int ret = 0;
if(root->val == targetSum) {
ret++;
}
ret += rootSum(root->left, targetSum - root->val);
ret += rootSum(root->right, targetSum - root->val);
return ret;
}
int pathSum(TreeNode* root, int targetSum) {
if(!root) {
return 0;
}
int ret = 0;
ret += rootSum(root, targetSum);
ret += pathSum(root->left, targetSum);
ret += pathSum(root->right, targetSum);
return ret;
}
};
class Solution {
public:
unordered_map<int, int> mp;
int rootSum(TreeNode* root, int ans,int targetSum) {
if(root == NULL ) {
return 0;
}
ans += root->val;
int ret = 0;
if(mp.count(ans - targetSum)) {
ret += mp[ans - targetSum];
}
mp[ans]++;
ret += rootSum(root->left, ans, targetSum);
ret += rootSum(root->right, ans, targetSum);
mp[ans]--;
return ret;
}
int pathSum(TreeNode* root, int targetSum) {
mp[0] = 1;
return rootSum(root, 0, targetSum);
}
};
10.4 根据身高重建队列
406. 根据身高重建队列https://leetcode-cn.com/problems/queue-reconstruction-by-height/
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
解释:
编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(), people.end(), [](const vector<int>& u, const vector<int>& v) {
return u[0] > v[0] || (u[0] == v[0] && u[1] < v[1]);
});
vector<vector<int>> ans;
for (const vector<int>& person: people) {
ans.insert(ans.begin() + person[1], person);
}
return ans;
}
};
第11天 困难
11.1 最长有效括号
32. 最长有效括号https://leetcode-cn.com/problems/longest-valid-parentheses/
给你一个只包含 '('
和 ')'
的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3:
输入:s = ""
输出:0
class Solution {
public:
int longestValidParentheses(string s) {
int size = s.length();
vector<int> dp(size, 0);
int maxVal = 0;
for(int i = 1; i < size; i++) {
if (s[i] == ')') {
if (s[i - 1] == '(') {
dp[i] = 2;
if (i - 2 >= 0) {
dp[i] = dp[i] + dp[i - 2];
}
} else if (dp[i - 1] > 0) {
if ((i - dp[i - 1] - 1) >= 0 && s[i - dp[i - 1] - 1] == '(') {
dp[i] = dp[i - 1] + 2;
if ((i - dp[i - 1] - 2) >= 0) {
dp[i] = dp[i] + dp[i - dp[i - 1] - 2];
}
}
}
}
maxVal = max(maxVal, dp[i]);
}
return maxVal;
}
};
class Solution {
public:
int longestValidParentheses(string s) {
stack<int> st;
for(int i = 0; i < s.size(); i++) {
if(s[i] == '(') {
st.push(i);
}
else if(!st.empty()) {
auto j = st.top();
st.pop();
s[j] = '1';
s[i] = '1';
}
}
int count = 0;
for(int i = 0; i < s.size(); i++) {
if(s[i] == '1') {
int l = i;
int r = i;
while(r < s.size() && s[r] == '1') {
r++;
}
count = max(count, r - l);
i = r;
}
}
return count;
}
};
第12天 困难 编辑距离
72. 编辑距离https://leetcode-cn.com/problems/edit-distance/
12.1 编辑距离
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
示例 1:
输入:word1 = "horse", word2 = "ros"
输出:3
解释:
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')
示例 2:
输入:word1 = "intention", word2 = "execution"
输出:5
解释:
intention -> inention (删除 't')
inention -> enention (将 'i' 替换为 'e')
enention -> exention (将 'n' 替换为 'x')
exention -> exection (将 'n' 替换为 'c')
exection -> execution (插入 'u')
动态规划:
class Solution {
public:
int minDistance(string word1, string word2) {
int m = word1.size();
int n = word2.size();
if(m * n == 0) return (m + n);
vector<vector<int>> dp(m + 1, vector(n + 1, 0));
for(int i = 0; i < m + 1 ; i++) {
dp[i][0] = i;
}
for(int j = 0; j < n + 1; j++) {
dp[0][j] = j;
}
for(int i = 1; i < m + 1; i++) {
for(int j = 1; j < n + 1; j++) {
if(word1[i - 1] == word2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1];
}
else {
dp[i][j] = 1 + min(dp[i - 1][j], min(dp[i][j - 1], dp[i - 1][j - 1]));
}
}
}
return dp[m][n];
}
};
13 天 二叉树 困难
13.1 二叉树中的最大路径和
124. 二叉树中的最大路径和https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/
路径 被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。
路径和 是路径中各节点值的总和。
给你一个二叉树的根节点 root ,返回其 最大路径和 。
class Solution {
public:
int myMax = INT32_MIN;
int recur(TreeNode* root) {
if(root == NULL) {
return 0;
}
int left = recur(root->left) + root->val;
int right = recur(root->right) + root->val;
int sum = left + right - root->val;
myMax = max(myMax, max(left, max(right, max(sum, root->val))));
return max(left, max(right, root->val));
}
int maxPathSum(TreeNode* root) {
recur(root);
return myMax;
}
};
13.2 二叉树的序列化与反序列化
297. 二叉树的序列化与反序列化https://leetcode-cn.com/problems/serialize-and-deserialize-binary-tree/
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string mys;
if(root == NULL) {
return mys;
}
queue<TreeNode *> qe;
qe.push(root);
while(!qe.empty()) {
int mSize = qe.size();
while(mSize--) {
auto t = qe.front();
if(t == NULL) {
mys += "none,";
}
else mys += to_string(t->val) + ',';
qe.pop();
if(t != NULL) {
qe.push(t->left);
}
if(t != NULL) {
qe.push(t->right);
}
}
}
return mys;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if(data.size() == 0) {
return NULL;
}
vector<string> mData;
int k = 0;
for(int i = 0; i < data.size(); i++) {
if(data[i] == ',') {
mData.push_back(data.substr(k, i - k));
k = i + 1;
}
}
TreeNode* head = new TreeNode(atoi(mData[0].c_str()));
queue<TreeNode*> qe;
qe.push(head);
int j = 1;
while(j < mData.size()) {
auto t = qe.front();
qe.pop();
if(mData[j] != "none") {
TreeNode* left = new TreeNode(atoi(mData[j].c_str()));
t->left = left;
qe.push(left);
}
if(j + 1< mData.size() && mData[j + 1] != "none") {
TreeNode* right = new TreeNode(atoi(mData[j + 1].c_str()));
t->right = right;
qe.push(right);
}
j++;
j++;
}
return head;
}
};
14 天区间问题汇总
14.1 用最少数量的箭引爆气球(找公共区间)
452. 用最少数量的箭引爆气球https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/
14.1.1 只需要统计其个数
class Solution {
public:
static bool cmp(vector<int> &a, vector<int> &b) {
if(a[0] == b[0]) {
return a[1] < b[1];
}
return a[0] < b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
sort(points.begin(), points.end(), cmp);
int count = 0;
for(int i = 0; i < points.size(); i++) {
int l = points[i][0];
int r = points[i][1];
while(i < points.size() - 1 && points[i + 1][0] <= r) {
l = max(l, points[i + 1][0]);
r = min(r, points[i + 1][1]);
i++;
}
count++;
}
return count;
}
};
14.1.2 将其公共区间找出来
class Solution {
public:
static bool cmp(vector<int> &a, vector<int> &b) {
if(a[0] == b[0]) {
return a[1] < b[1];
}
return a[0] < b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
sort(points.begin(), points.end(), cmp);
for(int i = 0; i < points.size(); i++) {
int l = points[i][0];
int r = points[i][1];
while(i < points.size() - 1 && points[i + 1][0] <= r) {
l = max(l, points[i + 1][0]);
r = min(r, points[i + 1][1]);
i++;
}
result.push_back({l, r});
}
return result.size();
}
};
优化:
class Solution {
public:
static bool cmp(vector<int> &a, vector<int> &b) {
if(a[0] == b[0]) {
return a[1] < b[1];
}
return a[0] < b[0];
}
int findMinArrowShots(vector<vector<int>>& points) {
sort(points.begin(), points.end(), cmp);
int pre = points[0][1];
int tol = 1;
for(int i = 1; i < points.size(); i++) {
if(pre < points[i][0]) {
pre = points[i][1];
tol++;
}
else pre = min(pre, points[i][1]);
}
return tol;
}
};
14.2 合并区间
56. 合并区间https://leetcode-cn.com/problems/merge-intervals/
class Solution {
public:
static bool cmp(vector<int>& a, vector<int>& b) {
if(a[0] == b[0]) {
return a[1] < b[1];
}
return a[0] < b[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
vector<vector<int>> result;
sort(intervals.begin(), intervals.end(), cmp);
for(int i = 0; i < intervals.size(); i++) {
int l = intervals[i][0];
int r = intervals[i][1];
while(i < (intervals.size() - 1) && intervals[i + 1][0] <= r ) {
r = max(r, intervals[i + 1][1]);
i++;
}
if(result.size() > 0 && result[result.size() - 1][1] >= l) result[result.size() - 1][1] = max(r, intervals[i][1]);
else result.push_back({l, r});
}
return result;
}
};
20220706
class Solution {
public:
static bool compare(vector<int>& x, vector<int>& y) {
if(x[0] == y[0]) {
return x[1] < y[1];
}
return x[0] < y[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), compare);
vector<vector<int>> result;
int left = intervals[0][0];
int right = intervals[0][1];
for(int i = 1; i < intervals.size(); i++) {
if(right < intervals[i][0]) {
result.push_back({left, right});
left = intervals[i][0];
right = intervals[i][1];
} else {
right = max(intervals[i][1], right);
}
}
result.push_back({left, right});
return result;
}
};
14.3 无重叠区间
435. 无重叠区间https://leetcode-cn.com/problems/non-overlapping-intervals/
class Solution {
public:
static bool cmp(vector<int> &a, vector<int> &b) {
if(a[0] == b[0]) {
return a[1] < b[1];
}
return a[0] < b[0];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
int pre = intervals[0][1];
int tol = 1;
for(int i = 1; i < intervals.size(); i++) {
if(pre <= intervals[i][0]) {
pre = intervals[i][1];
tol++;
}
else pre = min(pre, intervals[i][1]);
}
return intervals.size() - tol;
}
};
2022年5月十日 第二次
class Solution {
public:
static bool compare(vector<int>& x, vector<int>& y) {
if(x[0] == y[0]) {
return x[1] < y[1];
}
return x[0] < y[0];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
int m = intervals.size();
sort(intervals.begin(), intervals.end(), compare);
int count = 0;
int l = intervals[0][1];
for(int i = 1; i < m; i++) {
if(l <= intervals[i][0]) {
l = intervals[i][1];}
else {
l = min(l, intervals[i][1]);
count++;
}
}
return count;
}
};
[[9,12],[1,10],[4,11],[8,12],[3,9],[6,9],[6,7]] (容易错)
第15天 子数组/子序列 能被K整除的个数/数组题
15.1 和为 K 的子数组
560. 和为 K 的子数组https://leetcode-cn.com/problems/subarray-sum-equals-k/
给你一个整数数组
nums
和一个整数k
,请你统计并返回 该数组中和为k
的子数组的个数 。示例 1:
输入:nums = [1,1,1], k = 2 输出:2示例 2:
输入:nums = [1,2,3], k = 3 输出:2
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
int count = 0;
unordered_map<int, int> record;
record[0] = 1;;
int ans = 0;
for (int i = 0; i < nums.size(); i++) {
ans += nums[i];
if(record.find(ans - k) != record.end()) {
count += record[ans - k];
}
record[ans]++;
}
return count;
}
};
15.2 和可被 K 整除的子数组(字节2022年4月17日笔试第三题)
974. 和可被 K 整除的子数组https://leetcode-cn.com/problems/subarray-sums-divisible-by-k/
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k) {
unordered_map<int, int> record = {{0, 1}};
int sum = 0, ans = 0;
for (int elem: nums) {
sum += elem;
// 注意 C++ 取模的特殊性,当被除数为负数时取模结果为负数,需要纠正
int modulus = (sum % k + k) % k;
if (record.count(modulus)) {
ans += record[modulus];
}
++record[modulus];
}
return ans;
}
};
作者自己实现代码:
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k) {
int n = nums.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
int count = 0;
for(int i = 0; i< n; i++) {
dp[i][i] = nums[i];
if(dp[i][i] % k == 0) {
count++;
}
}
for(int i = n - 1; i >= 0; i--) {
for(int j = n - 1; j > i; j--) {
if(i + 1 < j - 1) {
dp[i][j] = dp[i + 1][j - 1] + nums[i] + nums[j];
}
else {
dp[i][j] = dp[i + 1][j] + nums[i];
}
if(dp[i][j] % k == 0) {
count++;
}
}
}
return count;
}
};
15.3 牛客:k序列
链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
给一个数组 a,长度为 n,若某个子序列中的和为 K 的倍数,那么这个序列被称为“K 序列”。现在要你 对数组 a 求出最长的子序列的长度,满足这个序列是 K 序列。
L-K序列_埃森哲杯第十六届上海大学程序设计联赛春季赛暨上海高校金马五校赛 (nowcoder.com)
参考:K序列 最长子序列和%k==0 - 代码先锋网 (codeleading.com)
# include<iostream>
# include<vector>
using namespace std;
int count = 0;
void bank(vector<int>& num, int start, int sum, int K, int si) {
if(sum % K == 0) {
count = max(si, count);
}
if(start >= num.size()) {
return;
}
bank(num, start + 1, sum + num[start], K, si + 1);
bank(num, start + 1, sum, K, si);
}
int main() {
int n, K;
cin >> n >> K;
vector<int> num;
int temp = n;
while(temp--) {
int t;
cin >> t;
num.push_back(t);
}
bank(num, 0, 0, K, 0);
cout << count << endl;
system("pause");
return 0;
}
15.4 牛客: 被3整除的子序列
参考:K序列(子序列的和能够被k整除、DP) - JmFv5 - 博客园 (cnblogs.com)
(1条消息) [算法] DP-被3整除的子序列_Orion_lin的博客-CSDN博客_被3整除的子序列
# include<iostream>
# include<vector>
# include<string>
using namespace std;
int main() {
string str;
cin >> str;
vector<vector<int>> dp(str.size() + 1, vector<int>(3, 0));
for(int i = 1; i <= str.size(); i++) {
if((str[i - 1] - '0') % 3 == 0) {
dp[i][0] = (dp[i - 1][0] + dp[i - 1][0] + 1) % 1000000007;
dp[i][1] = (dp[i - 1][1] + dp[i - 1][1]) % 1000000007;
dp[i][2] = (dp[i - 1][2] + dp[i - 1][2]) % 1000000007;
}
else if((str[i - 1] - '0') % 3 == 1) {
dp[i][0] = (dp[i - 1][0] + dp[i - 1][2]) % 1000000007;
dp[i][1] = (dp[i - 1][1] + dp[i - 1][0] + 1) % 1000000007;
dp[i][2] = (dp[i - 1][2] + dp[i - 1][1]) % 1000000007;
}
else if((str[i - 1] - '0') % 3 == 2) {
dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % 1000000007;
dp[i][1] = (dp[i - 1][1] + dp[i - 1][2]) % 1000000007;
dp[i][2] = (dp[i - 1][2] + dp[i - 1][0] + 1) % 1000000007;
}
}
cout << dp[str.size()][0] << endl;
system("pause");
return 0;
}
15.5 csdn : 最长被K整除的子串和
求数组元素和是K的倍数的子串的最大长度_二十六画生的博客的博客-CSDN博客
通过前缀和可以解决这一系列问题:
前缀和思想的理解与运用 - 力扣(LeetCode) (leetcode-cn.com)
15.6 删除最短的子数组使剩余数组有序
给你一个整数数组 arr ,请你删除一个子数组(可以为空),使得 arr 中剩下的元素是 非递减 的。
一个子数组指的是原数组中连续的一个子序列。
请你返回满足题目要求的最短子数组的长度。
示例 1:
输入:arr = [1,2,3,10,4,2,3,5]
输出:3
解释:我们需要删除的最短子数组是 [10,4,2] ,长度为 3 。剩余元素形成非递减数组 [1,2,3,3,5] 。
另一个正确的解为删除子数组 [3,10,4] 。
示例 2:
输入:arr = [5,4,3,2,1]
输出:4
解释:由于数组是严格递减的,我们只能保留一个元素。所以我们需要删除长度为 4 的子数组,要么删除 [5,4,3,2],要么删除 [4,3,2,1]。
示例 3:
输入:arr = [1,2,3]
输出:0
解释:数组已经是非递减的了,我们不需要删除任何元素。
示例 4:
输入:arr = [1]
输出:0
class Solution {
public:
int findLengthOfShortestSubarray(vector<int>& arr) {
int left = 1;
int right = arr.size() - 1;
while(left < arr.size() && arr[left - 1] <= arr[left]) {
left++;
}
while(right - 1 >= 0 && arr[right - 1] <= arr[right]) {
right--;
}
if(left > right) {
return 0;
}
int count = right;
for(int i = 0; i < left; i++) {
int j = lower_bound(arr.begin()+right, arr.end(), arr[i]) - arr.begin();
count = min(count, j - i - 1);
}
return count;
}
};
第 16天 面试过程中发现的力扣原题
1、2022年5月8日 天翼云笔试第三题 面试原题
402. 移掉 K 位数字https://leetcode-cn.com/problems/remove-k-digits/
给你一个以字符串表示的非负整数
num
和一个整数k
,移除这个数中的k
位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。示例 1 :
输入:num = "1432219", k = 3
输出:"1219"
解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。
示例 2 :输入:num = "10200", k = 1
输出:"200"
解释:移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。
示例 3 :输入:num = "10", k = 2
输出:"0"
解释:从原数字移除所有的数字,剩余为空就是 0 。来源:力扣(LeetCode)
class Solution {
public:
string removeKdigits(string num, int k) {
vector<char> stk;
for (auto& digit: num) {
while (stk.size() > 0 && stk.back() > digit && k) {
stk.pop_back();
k -= 1;
}
stk.push_back(digit);
}
for (; k > 0; --k) {
stk.pop_back();
}
string ans = "";
bool isLeadingZero = true;
for (auto& digit: stk) {
if (isLeadingZero && digit == '0') {
continue;
}
isLeadingZero = false;
ans += digit;
}
return ans == "" ? "0" : ans;
}
};
2 2022.5.18 奇安信笔试题 第二题
现需要对一批有序货物进行抽检。抽格时,取样品货物需要遵语以下规则,每次只能从前一个被袖检货物的后面相邻两个货物中选择,依次将整批货物抽检完毕,即,有n个货物,如果抽检了第件货物,那么下一件待抽检货物只能是第+1件货物或第+2件货物,直到没有货物可取成剩下最后一件货物时,抽恰才算结束。检查每件技物所耗费的时长不同。给定货物数goods 其中中goods[]是检查该件货物所需要的时长。可以选择从第1件或是第2件货物开始抽检。求计算完成抽检,所需要的最短时长。
1、递归
# include<iostream>
# include<vector>
# include<algorithm>
using namespace std;
int mcount = INT32_MAX;
void rob(vector<int>& coins, int position, int sSum) {
if (position >= coins.size()) {
mcount = min(sSum, mcount);
return;
}
//偷该位置
rob(coins, position + 1, sSum + coins[position]);
rob(coins, position + 2, sSum + coins[position]);
return;
}
int main() {
vector<int> coins = {2, 100, 2, 3, 3,90,3, 2, 10, 2};
rob(coins, 0, 0);
cout << mcount << endl;
system("pause");
return 0;
}
2、动态规划
# include<iostream>
# include<vector>
using namespace std;
int main() {
vector<int> coins = {2, 100, 2, 3, 3,90,3, 2, 10, 2};
vector<int> dp(coins.size(), 0);
dp[0] = coins[0];
dp[1] = coins[1];
for (int i = 2; i < coins.size(); i++) {
dp[i] = min(dp[i - 2], dp[i - 1]) + coins[i];
}
cout << min(dp[coins.size() - 1], dp[coins.size() - 2]) << endl;
system("pause");
return 0;
}
15天 广度优先搜索
这类题在华为以及网易的笔试中都遇到过,出现频率也挺高的。
542. 01 矩阵https://leetcode.cn/problems/01-matrix/
给定一个由 0 和 1 组成的矩阵 mat ,请输出一个大小相同的矩阵,其中每一个格子是 mat 中对应位置元素到最近的 0 的距离。
两个相邻元素间的距离为 1 。
输入:mat = [[0,0,0],[0,1,0],[0,0,0]]
输出:[[0,0,0],[0,1,0],[0,0,0]]
示例 2:
输入:mat = [[0,0,0],[0,1,0],[1,1,1]]
输出:[[0,0,0],[0,1,0],[1,2,1]]
class Solution {
public:
const int dist[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
vector<vector<int>> updateMatrix(vector<vector<int>>& mat) {
int n = mat.size();
int m = mat[0].size();
vector<vector<int>> visit(n, vector<int>(m, INT32_MAX));
queue<pair<int, int>> qe;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(mat[i][j] == 0) {
visit[i][j] = 0;
qe.push({i, j});
}
}
}
while(!qe.empty()) {
auto t = qe.front();
qe.pop();
int x = t.first;
int y = t.second;
for(int i = 0; i < 4; i++) {
int dx = x + dist[i][0];
int dy = y + dist[i][1];
if(dx >= 0 && dx < n && dy >= 0 && dy < m) {
if(visit[dx][dy] > visit[x][y] + 1) {
visit[dx][dy] = visit[x][y] + 1;
qe.push({dx, dy});
}
}
}
}
return visit;
}
};
美团测开笔试第二题:
#include <iostream>
#include <vector>
#include <string>
#include <unordered_map>
using namespace std;
double result = 0;
unordered_map<int, double> mp;
void count(int n, int m, vector<int>& p, vector<int>& score, int position, double temp) {
if (position == n) {
result = max(result, temp);
return;
}
if (m != 0) {
count(n, m, p, score, position + 1, temp + (double)(score[position] * p[position]) / 100.0);
count(n, m - 1, p, score, position + 1, temp + (double)score[position]);
}
else {
for (int i = position; i < n; i++) {
temp += (double)(score[i] * p[i]) / 100.0;
}
result = max(result, temp);
}
return;
}
int main() {
int n;
int m;
cin >> n >> m;
int t = n;
vector<int> p;
while (t--) {
int temp;
cin >> temp;
p.push_back(temp);
}
t = n;
vector<int> score;
while (t--) {
int temp;
cin >> temp;
score.push_back(temp);
}
vector<vector<double>> dp(n, vector<double>(m + 1, 0.0));
for (int i = 0; i <= m; i++) {
if (i == 0) {
dp[0][i] = (double)(score[0] * p[0]) / 100.0;
}
else {
dp[0][i] = (double)(score[0]);
}
}
for (int i = 1; i < n; i++) {
for (int j = 0; j <= m; j++) {
if (j == 0 && j < m) dp[i][j] = max(dp[i - 1][j], dp[i - 1][j + 1]) + (double)(score[i] * p[i]) / 100.0;
else if (j == 0 && j == m) {
dp[i][j] = dp[i - 1][j] + (double)(score[i] * p[i]) / 100.0;
}
else if(j < m){
dp[i][j] = max(dp[i - 1][0], dp[i - 1][j + 1]) + (double)(score[i]);
}
else if (j == m) {
dp[i][j] = dp[i - 1][0] + (double)(score[i]);
}
}
}
for (int i = 0; i <= m; i++) {
result = max(dp[n - 1][i], result);
}
cout << result << endl;
system("pause");
return 0;
}