力扣做题笔记(一)

昨晚在LeetCode上开始刷算法题,本来以为已经学了那么多,从简单题刷起的我应该是一路畅通无阻,然而现实好像不是这样的。

我会很认真的记录并讲解,虽然我知道起这么个文章标题应该没什么会看到。但是相见即是有缘嘛。

一篇三题啊,不多也不少。

文章目录

      • 第一题(删除有序数组中的重复项)
      • 第二题(买卖股票的最佳时机)
      • 第三题(旋转数组)
      • 我的题解(题1)
      • 我的题解(题2)
      • 我的题解(3)
      • 官方题解(1)
      • 官方题解(2)
      • 官方题解(3)
      • 该学到啥

第一题(删除有序数组中的重复项)

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 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) {

   }
};

我的题解(题1)

由于题目限定了STL,我也改过它的参数,是不允许的,所以就按题目的要求来了。

由于要遍历数据与删除,所以我直接就想到了迭代器,毕竟我自己在别的vector那篇博客里写了,术业有专攻,访问容器就用迭代器,不然人家封装给你干嘛?

其实也挺好,但是就怪我自己读书不认真吧。

vector的迭代器是支持双向的!
vector的删除函数是支持一个区间内删除的!

好,然后出现了一段莫名其妙的代码错误:

力扣做题笔记(一)_第1张图片
那两个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();
        
    }

就改成这样儿了,过是过了,但是我自己不是很满意,因为时间复杂度过高。

所以我就一直改,反正最后也没优化出来。直到看了讨论区的其他人,我才知道差距在哪里。
(官方解释在下面)


我的题解(题2)

题二其实一看是很直白的,不过经过前一题的摧残,我觉得又不删除,搞个迭代器等下遍历都烦。所以我就用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;
    }

这里面需要注意的是出现连续递增的情况,这题我的算法复杂度排名还是很靠前的。


我的题解(3)

这题居然放在了简单题,应该是我太菜了。

我是这样想的,肯定不能是一次挪一格,这样慢吞吞的,要一步到位!

然后问题就来了。要怎么一步到位?因为空间复杂度的限制。
我决定使用环状替换。

力扣做题笔记(一)_第2张图片

这个过程充满了艰辛与曲折,因为要考虑的情况实在太多了,首先就是活死环,有的环它没办法把你全部的数据全部囊括进去啊!!!就像上面那样,用了两个环。

然后就是数据的转换,照上面,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;
        }
    }
}

别看我代码长,算法复杂度其实还是排在前三分之一的。


官方题解(1)

看完官方题解的算法之后,我觉得我的算法是真的菜。
主要区别有以下两点:

  1. 人家不删除,只是把不重复的项前移。
  2. 他们使用的是快慢指针,快指针负责找下一个不重复项,慢指针负责记载当前不重复项的末尾。快慢指针结合即可重复排序。

来看一下他们的代码:

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++版本的。


官方题解(2)

他们的算法和我的差不多,遇到波峰就卖出,找到波谷再买入。


官方题解(3)

他们有四个算法,第一个就是一步一步挪的暴力算法,第二个使用了额外数组,第三个就是我这种。
不提了,

方法 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的基本使用是要掌握的。
然后那个波峰波谷法也要会。

其实更应该学会的是那个快慢指针,个人觉得应用场景挺好的。

你可能感兴趣的:(力扣做题笔记(一))