动态规划
链表
位运算
回文子串:最长回文子串,删掉一个字符变为回文串
动态规划
198.打家劫舍
题目: 一排房屋,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
解法:动态规划。 dp[i]存放偷到第i个房子时的最大金额,转移方程dp[i] = max(dp[i-1], dp[i-2] + nums[i]).
如果偷第i个那么第i-1个肯定不偷,只要看dp[i-2] + nums[i]是不是比dp[i-1]大就可以了。
int rob(vector<int>& nums) {
if(nums.size() == 0) return 0;
if(nums.size() == 1) return nums[0];
if(nums.size() == 2) return max(nums[0], nums[1]);
vector<int> dp(nums.size(), 0);
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for(int i=2; i < nums.size(); i++){
if(dp[i-1] > dp[i-2] + nums[i]){
dp[i] = dp[i-1];
}
else{
dp[i] = dp[i-2] + nums[i];
}
}
return dp[nums.size() - 1];
}
进阶版:房屋首尾相连 213. 打家劫舍 II
二维,dp[0][i]表示不偷第一家,dp[1][i]表示偷第一家,最后一家的时候分开讨论。
int rob(vector<int>& nums) {
if(nums.size() == 0) return 0;
if(nums.size() == 1) return nums[0];
if(nums.size() == 2) return max(nums[0], nums[1]);
vector<vector<int>> dp(2, vector<int>(nums.size(), 0));
dp[0][0] = 0;
dp[0][1] = nums[1];
dp[1][0] = nums[0];
dp[1][1] = nums[0];
for(int j = 0; j < 2; j++){
for(int i=2; i < nums.size()-1; i++){
if(dp[j][i-1] > dp[j][i-2] + nums[i]){
dp[j][i] = dp[j][i-1];
}
else{
dp[j][i] = dp[j][i-2] + nums[i];
}
}
}
dp[0][nums.size() - 1] = max(dp[0][nums.size() - 2], dp[0][nums.size() - 3] + nums[nums.size() - 1]);
dp[1][nums.size() - 1] = dp[1][nums.size() - 2];
return max(dp[0][nums.size() - 1], dp[1][nums.size() - 1]);
}
41. 缺失的第一个正数
题目:给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
进阶:你可以实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案吗?
解法1:哈希表
解法2:置换。
step1: 扫描数组,如果x=nums[i],那么nums[i]应该放在nums[x-1]处。直到nums[i]不在[1,n]的范围内,扫下一个数。
step2: 扫描数组,如果nums[i] != i+1,那么返回i+1, 如果扫完一边都符合,返回n+1
int firstMissingPositive(vector<int>& nums) {
for(int i=0; i < nums.size(); i++){
while(nums[i] >0 && nums[i] <= nums.size() && nums[i] != nums[nums[i]-1]){
int temp = nums[nums[i]-1];
nums[nums[i]-1] = nums[i];
nums[i] = temp;
}
}
for(int i=0; i < nums.size(); i++){
if(nums[i] != i+1) return i+1;
}
return nums.size()+1;
}
链表
142. 环形链表 II
题目:给你个链表,返回环的入口结点
官方解法:a+(n+1)
b+nc=2(a+b)⟹a=c+(n−1)(b+c),快慢指针相遇后,额外使用一个指针 ptr。起始,它指向链表头部;随后,它和slow 每次向后移动一个位置。最终,它们会在入环点相遇。
解法2:快慢指针 先找到环中有多少节点,然后相当于求链表倒数第n个节点(采用滑动窗口),代码如下。
class Solution {
public:
ListNode *find_node(ListNode *head, int n){
ListNode *begin = head, *end = head;
while(--n){
end = end->next;
}
while(end->next != begin){
// cout << begin->val << '\t' << end->val << endl;
begin = begin->next;
end = end->next;
}
return begin;
}
ListNode *detectCycle(ListNode *head) {
if(!head || !head->next) return nullptr;
ListNode *slow = head;
ListNode *quick = head->next;
int count = 1;
while(quick!=slow){
quick = quick->next;
if(!quick) return nullptr;
quick = quick->next;
if(!quick) return nullptr;
slow = slow->next;
count++;
}
// cout << count << endl;
ListNode *temp = find_node(head, count);
return temp;
}
};
141. 环形链表
题目:给你个链表,判断有没有环
快慢指针「Floyd 判圈算法」(又称龟兔赛跑算法)
定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head && !head->next) return false;
if(!head) return false;
ListNode *slow = head;
ListNode *quick = head->next;
while(quick != slow ){
quick = quick->next;
if(!quick) return false;
quick = quick->next;
if(!quick) return false;
slow = slow->next;
}
return true;
}
};