LeetCode初级算法(动态规划+设计问题篇)

时间:2021-11-06 16:22:43

爬楼梯

第一想法自然是递归,而且爬楼梯很明显是一个斐波拉切数列,所以就有了以下代码:

class Solution {
public:
int climbStairs(int n) {
if(n==0) return 0;
if(n==1) return 1;
if(n==2) return 2;
if(n>2)
{
return (climbStairs(n-1)+climbStairs(n-2));
}
}
};

但是在输入为44的时候提示超出时间限制了,仔细想想的确如此,反复递归、调用函数的开销还是挺大的。所以如果换成循环会不会好一点呢?

class Solution {
public:
int climbStairs(int n) {
if(n==0) return 0;
vector<int> res(n);
res[0]=1;
res[1]=2;
for(int i=2;i<n;i++)
{
res[i]=res[i-1]+res[i-2];
}
return res[n-1];
}
};

结果没有超时,用循环的开销还是可以的。

买卖股票的最佳时机

之前记得数组篇里写了买卖股票的最佳时机类似的题,但是这题却和那道题不太一样。这题要求的是当前的值和之前的最小值的差值为最大,所以如果输入的数据是递减的话,后面减去前面只会一直为负值所以最大李瑞为0。这里我们用buy来存储前面的最小值,用res来存储当前的最大利润,代码如下:

class Solution {
public:
int maxProfit(vector<int>& prices) {
int res=0;
int buy=INT_MAX;
for(int i=0;i<prices.size();i++)
{
buy=min(prices[i],buy);
res=max(res,prices[i]-buy);
}
return res;
}
};

最大子序和

由于涉及到连续和,所以觉得用循环来做应该还可以。想法就是把所有的和存储到一个容器里,然后简单排序一下取最大的就ok了,代码如下:

class Solution {
public:
int maxSubArray(vector<int>& nums) {
vector<int> a;
if(nums.size()==1) return nums[0];
for(int i=0;i<nums.size();i++)
{
int sum=nums[i];
a.push_back(sum);
for(int j=i+1;j<nums.size();j++)
{
sum+=nums[j];
a.push_back(sum);
}
}
sort(a.begin(),a.end());
return a[a.size()-1];
}
};

但是,果然还是由于输入量如果过大的话,会开销很大,所以超出时间限制了。所以换了思路,采用如下的代码:

class Solution {
public:
int maxSubArray(vector<int>& nums) {
int res=INT_MIN;
int curSum=0;
for(int i=0;i<nums.size();i++)
{
curSum=max(curSum+nums[i],nums[i]);
res=max(res,curSum);
}
return res;
}
};

打家劫舍

这道题的意思其实就是,一个数组,求 不取相邻的数 的和的最大值。下面是动态规划的解法:

class Solution {
public:
int rob(vector<int>& nums) {
if(nums.size()<=1) return nums.empty()?0:nums[0];
vector<int> dp={nums[0],max(nums[0],nums[1])};
for(int i=2;i<nums.size();i++)
dp.push_back(max(nums[i]+dp[i-2],dp[i-1]));
return dp.back();
}
};

此外,还有另外一种思路,见大神的代码:

class Solution {
public:
int rob(vector<int>& nums) {
intfor(inti0;i<nums.size();i++)
{
if(i%2==0)
{
a+=nums[i];
a=max(a,b);
}
else
{
b+=nums[i];
b=max(a,b);
}
}
return max(a,b);
}
};

这段代码更容易理解,只是不知道是如何想出来的。

动态规划小结

感觉动态规划问题就是一定要注意前面计算的结果和即将计算结果之间的联系,妥善处理它们之间的表达式就会有正确的思路。感觉自己在这种问题上的思考深度还是不够,具有局限性,刷完初级算法后会专门来学习一下动态规划问题的解法。

Shuffle an Array

这道题思路很简单,但主要是考察几个函数之间的调换关系。比如需要创建爱你一个私有类来声明vec这个数组用于全局。见代码:

class Solution {
public:
Solution(vector<int> nums):vec(nums) { } /** Resets the array to its original configuration and return it. */
vector<int> reset() {
return vec;
} /** Returns a random shuffling of the array. */
vector<int> shuffle() {
vector<int> res=vec;
for(int i=0;i<res.size();i++)
{
int pos =rand() % (res.size() - i);
swap(res[i], res[i+pos]);
}
return res;
} private:
vector<int> vec;
}; /**
* Your Solution object will be instantiated and called as such:
* Solution obj = new Solution(nums);
* vector<int> param_1 = obj.reset();
* vector<int> param_2 = obj.shuffle();
*/

最小栈

这道题觉得可以构造一个vector容器,然后利用vector的相关成员函数来解决,当时觉得题目既然叫"最小栈",呢必然是用其他的方式去实现栈的操作呀,所以自己的代码如下:

class MinStack {
public:
/** initialize your data structure here. */
MinStack() { } void push(int x) {
res.push_back(x);
} void pop() {
res.pop_back();
} int top() {
if(res.size()==0) return NULL;
return res.back();
} int getMin() {
if(res.size()==0) return NULL;
vector<int> temp=res;
sort(temp.begin(),temp.end());
return temp[0];
} private:
vector<int> res;
};

上面的代码我自己测了几个案例是可以的,但是在提交的时候却显示超出时间限制,简单的少了一眼大神的代码,发现是直接用stack来做的。。。可是如果stack能通过的话,vector怎么会通不过呢?先放上大神的代码,直接用stack的话比较好理解:

class MinStack {
public:
/** initialize your data structure here. */
MinStack() { } void push(int x) {
s1.push(x);
if(s2.empty()||x<=s2.top())
s2.push(x);
} void pop() {
if(s1.top()==s2.top())
s2.pop();
s1.pop();
} int top() {
return s1.top();
} int getMin() {
return s2.top();
} private:
stack<int> s1,s2;
};

在网上搜了一下,也有人用vector做出来了:

class MinStack {
public:
vector<int> allVec;
vector<int> minVec; void push(int x) {
if (allVec.empty()) {
allVec.push_back(x);
minVec.push_back(x);
}
else {
if (x <= minVec[minVec.size() - 1]) {
minVec.push_back(x);
}
allVec.push_back(x);
}
} void pop() {
if (allVec[allVec.size() - 1] == minVec[minVec.size() - 1])
minVec.erase(minVec.end() - 1);
allVec.erase(allVec.end() - 1);
} int top() {
return allVec[allVec.size() - 1];
} int getMin() {
return minVec[minVec.size() - 1];
}
};

他这样解的思路在于,设置里两个vector,一个是存储所有的元素,另一个是存储最小元素。首先看push(),如果为空就push进去;不为空就判断如果比minVec中最后一个元素要小,就放进minVec中、allVec是始终都要放得。再看pop(),如果allVec[allVec.size() - 1] == minVec[minVec.size() - 1],即如果要删除的元素是最小值的话,就必须将minVec最后一个元素也删掉,确保minVec中存储的都是有效的最小值;allVec直接将最后一个元素清除。top()则是返回allVec最后一个元素,getMin()是返回minVec的最后一个元素。和自己的代码作比较,感觉自己花了两个vector存储一样的数组并且当输入巨大的时候,数组复制一定耗费了大量的时间所以会导致超出时间限制。

但是不得不说,vector确实相较stack来说效率有点低。