昨晚在LeetCode上开始刷算法题,本来以为已经学了那么多,从简单题刷起的我应该是一路畅通无阻,然而现实好像不是这样的。
我会很认真的记录并讲解,虽然我知道起这么个文章标题应该没什么会看到。但是相见即是有缘嘛。
一篇三题啊,不多也不少。
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
int removeDuplicates(vector<int>& nums)
{
}
};
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
int maxProfit(vector<int>& prices) {
}
};
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
示例 2:
输入: [-1,-100,3,99] 和 k = 2
输出: [3,99,-1,-100]
解释:
向右旋转 1 步: [99,-1,-100,3]
向右旋转 2 步: [3,99,-1,-100]
说明:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
要求使用空间复杂度为 O(1) 的 原地 算法。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/rotate-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
class Solution {
public:
void rotate(vector<int>& nums, int k) {
}
};
由于题目限定了STL,我也改过它的参数,是不允许的,所以就按题目的要求来了。
由于要遍历数据与删除,所以我直接就想到了迭代器,毕竟我自己在别的vector那篇博客里写了,术业有专攻,访问容器就用迭代器,不然人家封装给你干嘛?
其实也挺好,但是就怪我自己读书不认真吧。
vector的迭代器是支持双向的!
vector的删除函数是支持一个区间内删除的!
好,然后出现了一段莫名其妙的代码错误:
那两个12不用管,是容器大小而已,我用的测试数据是{1,5,7,4,4,4,3,3,7,6,7,7}
这段是用来测试的,不过这个结果显然让我挺失望的。不知道是不是我小学数学都没学好。
其实后来想想也挺正常,那个“++”的位置要放好!!!
改完之后呢,我就交了第一份代码上去。
int removeDuplicates(vector<int>& nums) {
vector<int>::iterator it = nums.begin();
int temp=*it;
for(it++;it!=nums.end();)
{
if(temp==*it)
nums.erase(it);
else
{
temp=*it;
it++;
}
}
return nums.size();
}
然后果然被甩回来了,为啥呢?用了个空数据集我就崩了
于是我又改。
int removeDuplicates(vector<int>& nums) {
if(nums.size() == 0)
return 0;
vector<int>::iterator it = nums.begin();
int temp=*it;
for(it++;it!=nums.end();)
{
if(temp==*it)
nums.erase(it);
else
{
temp=*it;
it++;
}
}
return nums.size();
}
就改成这样儿了,过是过了,但是我自己不是很满意,因为时间复杂度过高。
所以我就一直改,反正最后也没优化出来。直到看了讨论区的其他人,我才知道差距在哪里。
(官方解释在下面)
题二其实一看是很直白的,不过经过前一题的摧残,我觉得又不删除,搞个迭代器等下遍历都烦。所以我就用at了。
这题没什么波折,认真看好题目就行。
我的做法是这样的:
int maxProfit(vector<int>& prices) {
int a = prices.size();
if(a == 0)
return 0;
int i,j,k,sum;
for(i = 0,j = 1,sum = 0 ;i<a-1,j<a;)
{
k = j+1;
if(prices.at(i)<prices.at(j) && j<a)
{
while (k<a&&prices.at(k)>=prices.at(j)) {
j=k;
k++;
}
sum+=prices.at(j)-prices.at(i);
}
i = j;
j++;
}
return sum;
}
这里面需要注意的是出现连续递增的情况,这题我的算法复杂度排名还是很靠前的。
这题居然放在了简单题,应该是我太菜了。
我是这样想的,肯定不能是一次挪一格,这样慢吞吞的,要一步到位!
然后问题就来了。要怎么一步到位?因为空间复杂度的限制。
我决定使用环状替换。
这个过程充满了艰辛与曲折,因为要考虑的情况实在太多了,首先就是活死环,有的环它没办法把你全部的数据全部囊括进去啊!!!就像上面那样,用了两个环。
然后就是数据的转换,照上面,1插入3,得把3先取出来,3插入5,得把5先取出来,再把3放进去,如果还有7,还有9,还有11呢?这个临时变量要怎么分配?也是个问题。
分配好了,那环内的跳转呢?从一个环到另一个环的跳转呢?这也要考虑。
最后还有那些刁钻的测试题库。
经过重重磨难,我还是把它实现了,不为什么,就因为我想。
void rotate(vector<int>& nums, int k)
{
int sz = nums.size();
if(sz <2 || k == 0) //如果为空,退出
return;
int home = 0; //记录起点位置,1、如果一环结束,作为结束标志位;2、如果非一环结束,作为起点
int get_node1,get_node2; //获取两个节点
get_node1 = nums.at(0);
int current_node1 = 0;
int current_node2;
int count = 0; //当前已经移动了多少个节点
while( count != sz ) //当不是所有节点都走完的时候
{
current_node2 = (current_node1+k)%sz;
//将两个点都取出来
if(current_node2 == current_node1) //这里来判断是否需要转换,
//那个题库可刁钻了,比方说 [1,2] 2
return;
get_node2 = nums.at(current_node2);//调试主要看这步
nums.at(current_node2) = get_node1; //这步
current_node1 = current_node2;//这步
get_node1 = get_node2;//还有这步
count++;
if(home == current_node1)// 如果环成环了
{
current_node1+=1;
get_node1 = nums.at(current_node1); //换环之后,需要重新配置元素,不然会出问题
home+=1;
}
}
}
别看我代码长,算法复杂度其实还是排在前三分之一的。
看完官方题解的算法之后,我觉得我的算法是真的菜。
主要区别有以下两点:
来看一下他们的代码:
int maxProfit(vector<int>& prices) {
if (nums.size() == 0) return 0;
int i = 0;
for (int j = 1; j < nums.size(); j++) {
if (nums[j] != nums[i]) {
i++;
nums[i] = nums[j];
}
}
return i + 1;
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shan-chu-pai-xu-shu-zu-zhong-de-zhong-fu-xiang-by-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
他们用java写的,我改成C++版本的。
他们的算法和我的差不多,遇到波峰就卖出,找到波谷再买入。
他们有四个算法,第一个就是一步一步挪的暴力算法,第二个使用了额外数组,第三个就是我这种。
不提了,
方法 4:使用反转
算法
这个方法基于这个事实:当我们旋转数组 k 次, k%nk%n 个尾部元素会被移动到头部,剩下的元素会被向后移动。
在这个方法中,我们首先将所有元素反转。然后反转前 k 个元素,再反转后面 n-kn−k 个元素,就能得到想要的结果。
假设 n=7n=7 且 k=3k=3 。
原始数组 : 1 2 3 4 5 6 7
反转所有数字后 : 7 6 5 4 3 2 1
反转前 k 个数字后 : 5 6 7 4 3 2 1
反转后 n-k 个数字后 : 5 6 7 1 2 3 4 --> 结果
public class Solution {
public void rotate(int[] nums, int k) {
k %= nums.length;
reverse(nums, 0, nums.length - 1);
reverse(nums, 0, k - 1);
reverse(nums, k, nums.length - 1);
}
public void reverse(int[] nums, int start, int end) {
while (start < end) {
int temp = nums[start];
nums[start] = nums[end];
nums[end] = temp;
start++;
end--;
}
}
}
复杂度分析
时间复杂度:O(n)O(n) 。 nn 个元素被反转了总共 3 次。
空间复杂度:O(1)O(1) 。 没有使用额外的空间。
作者:LeetCode
链接:https://leetcode-cn.com/problems/rotate-array/solution/xuan-zhuan-shu-zu-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
这个构思是挺巧妙的。
首先对于vector的基本使用是要掌握的。
然后那个波峰波谷法也要会。
其实更应该学会的是那个快慢指针,个人觉得应用场景挺好的。