搜索旋转排序数组 II
解题思路,这道题因为之前做过,所以肯定是用二分法来解题。而且排序数组,看到排序也会想到是用二分法来降低时间复杂度。这道题和之前的不一样的地方在于可能包含重复数组,所以这个点是解决这道题的关键。
之前解决的时候是三种情况:
- 找到则返回true
nums[l] < nums[mid]
,说明前半部分有序,然后根据target来判断是在前面的还是后面的nums[l] > nums[mid]
,说明后半部分有序,然后根据target来判断是在前面的还是后面的
这道题因为多了一个重复的条件,所以会多出一种情况,即
nums[l] == nums[mid]
,因为这样的话是无法判断前面有序还是后面有序,所以处理时通过l++
去除干扰项。
AC代码:
class Solution {
public:
bool search(vector<int>& nums, int target) {
int l = 0, r = nums.size()-1;
while(l <= r) {
int mid = l + (r - l) / 2;
if (nums[mid] == target) return true;
if (nums[l] == nums[mid]) {
l++;
}
else if (nums[l] < nums[mid]) {
if (target >= nums[l] && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (target <= nums[r] && target > nums[mid]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return false;
}
};
删除排序链表中的重复元素 II
解题思路,删除重复的数字比较容易想到的用三个指针操作,第一个指针保存不重复的数字,第二三个指针用于搜索数字,如果第二三个指针数字不相同则把第二个指针保存,若相同继续递增第三个指针,直到和第二个指针数字不相同为止。
AC代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head == NULL || head->next == NULL) return head;
ListNode *pre = new ListNode(-1);
ListNode *second = head;
ListNode *res = pre;
while(second->next != NULL) {
ListNode *first = second;
second = second->next;
//相邻的两个指针值不同,则第一个指针可以被收入
if(first->val != second -> val) {
pre->next = first;
pre = first;
}else {
while(second->next != NULL && second->val == first->val) {
second = second->next;
}
}
//对最后一个值进行判断
if(second->next == NULL && second->val != first->val) {
pre->next = second;
pre = second;
}
}
pre->next = NULL;
return res->next;
}
};
删除排序链表中的重复元素
和上一道题相比,这道题容易不少,因为这道题可以用双指针就可以解决。但是思路确实差不多的。第一个指针确定数字,第二个指针用于扫描判断下一个数字。
AC代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head == NULL || head->next == NULL) return head;
ListNode* scan = head;
while(scan->next != NULL) {
ListNode* pre = scan;
scan = scan->next;
while(scan->next != NULL && pre->val == scan->val) {
scan = scan->next;
}
if(pre->val != scan->val) pre->next = scan;
else pre->next = NULL;
}
return head;
}
};
柱状图中最大的矩形
乍一看,感觉这道题和之前的最大盛水题目类似,所以想着用双指针,发现双指针无法解决问题,然后想到用动态规划,暴力当然超时。
无奈之下,看题解,大概的意思就是找到每个点的左右边界,而这个左右边界满足的条件是左右两边都必须大于等于这个点,然后找到最大值。
AC代码:
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
vector<int> left(n), right(n, n);
stack<int> mono_stack;
for (int i = 0; i < n; ++i) {
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
right[mono_stack.top()] = i;
mono_stack.pop();
}
left[i] = (mono_stack.empty() ? -1 : mono_stack.top());
mono_stack.push(i);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
};
最大矩形
又是没有解出来的一题,等会儿中午再理一下思路。
解题思路:
状态转移:
heights[i][j]表示点[i][j]的高度,由当前点和heights[i-1][j]共同决定。
matrix[i][j] == 0, heights[i][j] = 0; matrix[i][j] == 1, heights[i][j] = heights[i-1][j] + 1;
dp[i][j][k]表示点[i][j]在高度为k的时候最大面积,由前一个位置决定。
dp[i][j][k] = dp[i][j-1][k] + k;
AC代码:
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
int n = matrix.size();
int m = 0;
if (n > 0) { m = matrix[0].size(); }
vector<vector<int>> heights(n + 1,vector<int>(m + 1,0));
vector<vector<vector<int>>> dp(n + 1,vector<vector<int>>(m + 1, vector<int>(n + 1, 0)));
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (matrix[i-1][j-1] == '0') { continue; }
heights[i][j] = heights[i-1][j] + 1;
for (int k = 1; k <= heights[i][j]; k++) {
dp[i][j][k] = dp[i][j-1][k] + k;
ans = max(ans, dp[i][j][k]);
}
}
}
return ans;
}
};
分隔链表
解题思路,看了半天题目的意思,要求是小于x的都按照原来的顺序放到前面,大于等于x的顺序不变放到后面。明白要求之后就很清楚的知道如何去解决,定义两个哑结点分别指向小于x的链表和大于等于x的链表。
AC代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode *small = new ListNode(-1);
ListNode *res = new ListNode(-1);
res -> next = head;
ListNode *ope = res;
ListNode *sope = small;
while(ope != NULL && ope->next != NULL) {
ListNode* pre = ope;
ope = ope->next;
if(ope->val < x) {
sope->next = ope;
sope = ope;
pre->next = ope->next;
sope->next = NULL;
ope = pre;
}
}
sope->next = res->next;
return small->next;
}
};
扰乱字符串
解题思路,题目中的第一句话就表明了使用递归。
递归的思路,将给定的s1分成两部分 [0, i),[i, s1.size()-i), 1 < i < s1.size();
那么对应的s2可以是 [0, i),[i, s2.size()-i) 也可以是交换后的 [s2.size-i,s2.size)[0,s2.size()-i)。
需要注意的是,在递归的时候需要提前判断结束,否则会超时
AC代码:
class Solution {
public:
bool recursive(string s1, string s2) {
if(s1 == s2) return true;
//提前结束递归,防止超时
string t1 = s1, t2 = s2;
sort(t1.begin(), t1.end());
sort(t2.begin(), t2.end());
if(t1 != t2) return false;
for(int i = 1; i < s1.size(); ++i) {
bool f1 = recursive(s1.substr(0, i), s2.substr(0, i)) && recursive(s1.substr(i, s1.size() - i), s2.substr(i, s2.size() - i));
bool f2 = recursive(s1.substr(0, i), s2.substr(s2.size() - i, i)) && recursive(s1.substr(i, s1.size() - i), s2.substr(0, s2.size() - i));
if(f1 || f2) return true;
}
return false;
}
bool isScramble(string s1, string s2) {
if(s1.size() != s2.size()) return false;
return recursive(s1, s2);
}
};
合并两个有序数组
看到这道题,第一反应想到的是有序双链表合并,但是数组和链表不同的是,数组插入需要移动后面所有的位置,显然太麻烦。当然也可以直接调用vector的函数进行插入后排序,但是这样并不是最优解。最好的办法是从数组的后面对比插入,这样可以直接替换就可以了。
AC代码:
class Solution {
public:
void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
int p = m+n-1;
for(; n > 0; --n) {
if(m == 0 || nums2[n-1] >= nums1[m-1]) {
nums1[p--] = nums2[n-1];
}else {
while(m >=1 && nums1[m-1] >= nums2[n-1]) {
nums1[p--] = nums1[m-1];
--m;
}
nums1[p--] = nums2[n-1];
}
}
}
};
格雷编码
这道题更像一道找规律的题目,当你写出前几位的结果出来之后,可能就会发现规律。然后根据这个规律编码。
规律如下:
AC代码:
class Solution {
public:
vector<int> grayCode(int n) {
vector<int> res = {0};
if(n == 0) return res;
for(int i = 0; i < n; ++i) {
vector<int> tmp = res;
reverse(tmp.begin(), tmp.end());
for(auto t : tmp) {
res.push_back(t + (1 << i));
}
}
return res;
}
};
子集 II
解题思路,利用回溯的思想,把从个数0到nums.size()的子集全部添加到答案当中。利用排序 + 回溯时不等于前一回溯的首位 将重复的数字去除。
AC代码:
class Solution {
public:
vector<vector<int>> res;
vector<int> ope;
void recursive(int start, int len, vector<int>& nums) {
if(ope.size() == len) {
res.push_back(ope);
return;
}
for(int i = start; i < nums.size(); ++i) {
if(i != start && nums[i] == nums[i-1]) continue;
ope.push_back(nums[i]);
recursive(i+1, len, nums);
ope.pop_back();
}
}
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
res.push_back(ope);
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size(); ++i) {
recursive(0, i+1, nums);
}
return res;
}
};