日练三题,冰冻三尺非一日之寒。
写给自己的话:烦心事太多,先把心静一静,多出去走走,多说几句谢谢。
今日题目:1、房子小偷II;2、子集II;3、环形链表II。
今日摘录:
要么庸俗,要么孤独
——叔本华《要么孤独,要么庸俗》
213. House Robber II | Difficulty: Medium
Note: This is an extension of House Robber.
After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
题意:house robber的一个扩展,之前是一个数组,现在将其首尾相连,其他条件不变。
相关题目:房子小偷 房子小偷III
思路:
1、本来想用一个标志位来记录有没有选择数组起始位置的值,但是发现写来写去好像还是不太对,应该换一个角度来思考这个问题。
首先贴出house rober 的代码,看看这里没加限制条件之前的代码是怎么写的。
int rob(vector<int>& nums) {
int cur = 0,pre = 0;
for(int i=0;i<nums.size();i++)
{
int tmp = max(pre+nums[i],cur);
pre = cur;
cur = tmp;
}
return cur;
}
这里我们只需要从起点开始,一直到终点,然后逐个去计算状态。
但是题目现在改了,增加了一个限制就是头尾相连,这就是说选择了头就不能选择尾,选择了尾就不能选择头,那么是不是等同于将区间变成了[0,n-2]和[1,n-1],那么还是用原来的接口,给其设置一个起始和终止的位置,然后对[0,n-2]和[1,n-1]之间取一个最大值。
class Solution {
public:
int robSub(vector<int>& nums,int left,int right) {
int cur = 0,pre = 0;
for(int i=left;i<=right;i++)
{
int tmp = max(pre+nums[i],cur);
pre = cur;
cur = tmp;
}
return cur;
}
int rob(vector<int>& nums) {
int n = nums.size();
if(n<=1) return n?nums[0]:0;
return max(robSub(nums,0,n-2),robSub(nums,1,n-1));
}
};
结果:3ms
90. Subsets II | Difficulty: Medium
Given a collection of integers that might contain duplicates, nums, return all possible subsets.
Note: The solution set must not contain duplicate subsets.
For example,
If nums = [1,2,2], a solution is:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
相关题目:子集
题意:在子集一题的基础上增加了一个条件,就是集合中可以有相同的元素。
思路:
1、因为限制条件的增加,导致类似子集一提直接用二进制来表示集合中某个元素是否出现的方法不再适用,还是传统的回溯思想来解题。
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> res;
vector<int> tmp;
dfs(nums,0,res,tmp);
return res;
}
void dfs(const vector<int>&nums,int start,vector<vector<int>>&res,vector<int>&tmp)
{
res.push_back(tmp);
for(int i=start;i!=nums.size();i++)
{
if(i==start||nums[i]!=nums[i-1])
{
tmp.push_back(nums[i]);
dfs(nums,i+1,res,tmp);
tmp.pop_back();
}
}
}
};
结果:8ms
2、迭代版本,值得学习,代码来源:https://discuss.leetcode.com/topic/4661/c-solution-and-explanation
class Solution {
public:
vector<vector<int> > subsetsWithDup(vector<int> &S) {
vector<vector<int> > totalset = {{}};
sort(S.begin(),S.end());
for(int i=0; i<S.size();){
int count = 0; // num of elements are the same
while(count + i<S.size() && S[count+i]==S[i]) count++;
cout<<count<<endl;
int previousN = totalset.size();
cout<<previousN<<endl;
for(int k=0; k<previousN; k++){
vector<int> instance = totalset[k];
for(int j=0; j<count; j++){
instance.push_back(S[i]);
totalset.push_back(instance);
}
}
i += count;
}
return totalset;
}
};
142. Linked List Cycle II | Difficulty: Medium
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Note: Do not modify the linked list.
Follow up:
Can you solve it without using extra space?
相关题目:linked List Cycle
题意:给一个链表,判断环开始的地方,如果链表无环,返回null。
思路:
1、我的直观思路是首先用一快一慢两个链表找到这个链表环的大小,具体怎么找呢?假设快慢指针最终不能相遇,说明无环。
快指针a,慢指针b,
假设可以相遇,第一次相遇的时候慢指针走了x步,相遇点记做y。这个时候如果再有一个慢指针c从起点出发,那么这个指针是不是再走x步一定会和慢指针b相遇在y,但是这次b、c都是慢指针,它们相遇在y之前一定会先在链表环路的入口处相遇。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head,*slow=head;
if (head==NULL||head->next==NULL) return NULL;
bool isCycle = false;
while(slow!=NULL && fast!=NULL)
{
slow = slow->next;
if(fast->next==NULL) return NULL;
fast = fast->next->next;
if(fast==slow) {isCycle = true;break;}
}
if(!isCycle) return NULL;
fast = head;
while(fast!=slow)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
结果:12ms