LeetCode 81. 搜索旋转排序数组 II
分析
因为有重复数字的存在, 旋转后的数组第一个数 可能和最后一个数相等, 这样的话, 就不容易判断target
到底属于 前半段区间, 还是后半段区间.
因此需要加个while
循环, 排除重复元素, 并不影响结果的判定.
因为后一个区间的值全是小于nums[0]
的,
所以可以通过二分, 找到前半段区间的右端点的值, 即>= nums[0]
最靠右的值
然后让判断target
是否在所找的区间内部即可.
code
class Solution {
public:
bool search(vector<int>& nums, int target) {
if (nums.empty()) return false;
int R = nums.size() - 1;
while (R > 0 && nums[0] == nums[R]) R --;
if (R < 0) return nums[0] == nums[R]; // R < 0 说明整个数组全是同1个数
int l = 0, r = R;
while (l < r){
int mid = l + r + 1 >> 1;
if (nums[mid] >= nums[0]) l = mid;
else r = mid - 1;
}
if (target >= nums[0]) r = l, l = 0;
else l ++, r = R; // 注意l ++, 以下数据会导致越界
// 数据 1 3 0 在上一重二分输出 l = 1, r = 1
// 然后进入else 分支l ++, l = 2, r = 1, 导致l越界.
// 因此 最后return num[r] 而不是 nums[l]
while (l < r){
int mid = l + r >> 1;
if (nums[mid] >= target) r = mid;
else l = mid + 1;
}
return target == nums[r];
}
};
LeetCode 82. 删除排序链表中的重复元素 II
分析
注意 题目要求将所有相同的删掉, 一个都不保留, 相同的全部删掉
p代表重复元素的前一个元素
code
/**
* 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* deleteDuplicates(ListNode* head) {
auto dummy = new ListNode(-1);
dummy->next = head;
auto p = dummy;
while (p->next){
auto q = p->next->next;
while (q && q->val == p->next->val) q = q->next; //找出相同的一段[p, q)
if (p->next->next == q) p = p->next; //如果p = q, 那么表示相同的这一段只有一个数,那么p后移动一位
else p->next = q;// 否则, p的下一个位置指向q
}
return dummy->next;
}
};
LeetCode 83. 删除排序链表中的重复元素
分析
如果当前的值与下一个值相等, 则跳过p->next
, 即p->next = p->next->next
否则p往后走一步.
code
/**
* 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* deleteDuplicates(ListNode* head) {
if (!head) return head;
auto p = head;
while (p->next){
if (p->val == p->next->val) p->next = p->next->next;
else p = p->next;
}
return head;
}
};
LeetCode 84. 柱状图中最大的矩形
分析
单调栈, 分别对左边和右边做单调栈, 然后计算最大值即可
left 表示比当前低的竖条, 最左边界,
right 表示比当前低的竖条, 最右边界
code
class Solution {
public:
int largestRectangleArea(vector<int>& h) {
stack<int> stk;
int n = h.size();
vector<int> left(n), right(n);
for (int i = 0; i < n; i ++ ){
while (stk.size() && h[stk.top()] >= h[i]) stk.pop();
if (stk.empty()) left[i] = -1;
else left[i] = stk.top();
stk.push(i);
}
stk = stack<int>();
for (int i = n - 1; i >= 0; i -- ){
while (stk.size() && h[stk.top()] >= h[i]) stk.pop();
if (stk.empty()) right[i] = n;
else right[i] = stk.top();
stk.push(i);
}
int res = 0;
for (int i = 0; i < n; i ++ )
res = max(res, (right[i] - left[i] - 1) * h[i]);
return res;
}
};
LeetCode 85. 最大矩形
分析
只需计算出每层matrix
往上的最大高度, 然后对每一层应用上一题的最大矩形, 即可求出整个矩形的最大矩形.
code
class Solution {
public:
int largestRectangleArea(vector<int>& h) {
int n = h.size();
vector<int> left(n), right(n);
stack<int> stk;
for (int i = 0; i < n; i ++ ) {
while (stk.size() && h[stk.top()] >= h[i]) stk.pop();
if (stk.empty()) left[i] = -1;
else left[i] = stk.top();
stk.push(i);
}
stk = stack<int>();
for (int i = n - 1; i >= 0; i -- ) {
while (stk.size() && h[stk.top()] >= h[i]) stk.pop();
if (stk.empty()) right[i] = n;
else right[i] = stk.top();
stk.push(i);
}
int res = 0;
for (int i = 0; i < n; i ++ ) {
res = max(res, h[i] * (right[i] - left[i] - 1));
}
return res;
}
int maximalRectangle(vector<vector<char>>& matrix) {
if (matrix.empty() || matrix[0].empty()) return 0;
int n = matrix.size(), m = matrix[0].size();
vector<vector<int>> h(n, vector<int>(m));
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
if (matrix[i][j] == '1')
if (i) h[i][j] = h[i - 1][j] + 1;
else h[i][j] = 1;
int res = 0;
for (int i = 0; i < n; i ++ ) res = max(res, largestRectangleArea(h[i]));
return res;
}
};
LeetCode 86. 分隔链表
分析
类似于快排, 排序链表, 但不改变链表的相对顺序, 所以新建左右两个链表, 将<x的节点接到左边, >= x的接到右边, 然后左边尾部指向右边头部
code
/**
* 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* partition(ListNode* head, int x) {
auto lh = new ListNode(-1), rh = new ListNode(-1);
auto lt = lh, rt = rh;
for (auto p = head; p; p = p->next){
if (p->val < x) lt = lt->next = p;
else rt = rt->next = p;
}
lt->next = rh-> next;
rt->next = nullptr;
return lh->next;
}
};
LeetCode 87. 扰乱字符串
分析
按照题目要求, 两个字符串可以变成同一个, 只有当对两个字符串排序完, 每个字符对应, 就说明可以变成另一个.
所以递归枚举分割点
第一个字符串前i个 转换成 第二个字符串前i个
或者
第一个字符串前i个 转换成 第二个字符串后i个 并且
第一个字符串后n - i个s1.substr(i) 转换成 第二个字符串的前n - i个
code(leetcode数据改了,TLE了)
class Solution {
public:
bool isScramble(string s1, string s2) {
if (s1 == s2) return true;
int n = s1.size();
string ss1 = s1, ss2 = s2;
sort(ss1.begin(), ss1.end()), sort(ss2.begin(), ss2.end());
if (ss1 != ss2) return false;
for (int i = 1; i < n; i ++ ){
// 注意题目判断的s1 与 s2能否扰乱相同
// 因此下面必须对s1 和 s2递归, 不能对ss1 和 ss2 递归
if (isScramble(s1.substr(0, i), s2.substr(0, i)) && isScramble(s1.substr(i), s2.substr(i))) return true;
if (isScramble(s1.substr(0, i), s2.substr(n - i)) && isScramble(s1.substr(i), s2.substr(0, n - i))) return true;
}
return false;
}
};
code(dp)
class Solution {
public:
bool isScramble(string s1, string s2) {
int n = s1.size();
vector<vector<vector<bool>>> f(n, vector<vector<bool>>(n, vector<bool>(n + 1)));
for (int k = 1; k <= n; k ++ )
for (int i = 0; i + k - 1 < n; i ++ )
for (int j = 0; j + k - 1 < n; j ++ )
if (k == 1) {
if (s1[i] == s2[j]) f[i][j][k] = true;
}else {
for (int u = 1; u < k; u ++ )
if (f[i][j][u] && f[i + u][j + u][k - u] || f[i][j + k - u][u] && f[i + u][j][k - u]) {
f[i][j][k] = true;
break;
}
}
return f[0][0][n];
}
};
LeetCode 88. 合并两个有序数组
分析
归并排序的内容, 模版题
nums1
数组范围比较大, 因此倒着放置
code
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int k = n + m - 1;
int i = m - 1, j = n - 1;
while (i >= 0 && j >= 0){
if (nums1[i] >= nums2[j]) nums1[k --] = nums1[i --];
else nums1[k --] = nums2[j --];
}
while (j >= 0) nums1[k -- ] = nums2[j --];
// 因为最后一个循环是往nums1中放nums1剩余的数据, 本来就在位置上, 可以省略了
}
};
LeetCode 89. 格雷编码
分析
先末尾添加0, 相当于左移1位, 由于镜像复制, 需要立即push_back当前的数,
因此push_back(res[i] + 1)
code
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res;
res.push_back(0);
while (n -- ){
for (int i = res.size() - 1; i >= 0; i -- ){
res[i] *= 2;
res.push_back(res[i] + 1);
}
}
return res;
}
};
LeetCode 90. 子集 II
分析
for (int i = 0; i <= k - u; i ++){ ... }
递归树如下图
code
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end());
dfs(0, nums);
return res;
}
void dfs(int u, vector<int>& nums){
if (u == nums.size()){
res.push_back(path);
return;
}
int k = u;
while (k < nums.size() && nums[k] == nums[u]) k ++;
for (int i = 0; i <= k - u; i ++ ){
dfs(k, nums);
path.push_back(nums[u]);
}
for (int i = 0; i <= k - u; i ++ )
path.pop_back();
}
};