代码随想录算法训练营第一天 | 力扣题库位置:35 34 69 367 283 844 977

35.搜索插入位置

文档讲解:代码随想录

状态:已做出

思路 :

看完题目就可以看出来是使用二分进行作答,最后要求是使用时间复杂度为log的算法,更加说明了这个题目只能使用二分进行作答

二分相关思路文档:二分

看完代码随想录后思路更加清晰,首先就是要确定好二分的查找范围,我使用的是左闭右闭的范围,后面就是要确定好范围的最小值和最大值,left一般设置为下标为0开始,right一般设置为给出的数组最大下标,这样可以少浪费一些运行时间。接下来设置二分的循环,因为我是左闭右闭,所以循环条件是l<=r,随后就是二分经典的公式,当中间值的小标所在的位置的元素大于目标值时,r=mid-1,当小于时,l=mid+1,当值等于目标值后直接返回次下表mid,如果出循环了也没能找到目标值就直接返回l或者r(因为此时l和r相等)。

代码如下:

class Solution {
public:
    int searchInsert(vector& nums, int target) {
        int l=0;
        int r=nums.size()-1;
        while(l<=r)
        {
            int mid=l+((r-l)/2);
            if(nums[mid]>target)
                r=mid-1;
            else if(nums[mid]

遇到的困难:

首先就是二分本身,较难想的点就是怎么处理二分范围和每次查找的小标元素大于或者小于等于时怎么处理边界。为什么要在l<=r的情况下边界都需要设置为mid+1或mid-1,但是随后想通了,因为当mid下标元素大于r时,说明后面的值都已经全部都大于目标值了,l也是同理,这个范围因为是左右闭合的,所以大于和小于目标值的这两个数都不算在内,需要对l和r分别进行mid+1和mid-1来完全排除不符的区间。

其次的一个难点是在题目给出的,题目说如果找不到目标值,就需要返回目标值需要添加的小标,这和普通的二分有些许不同,普通的二分一旦没有找到目标值,结束循环后直接返回无(或者返回-1),关键在于跳出循环后l和r的值是不一样的,这时必须要判断出返回l还是r。随后思考后找到了规律,最后一个循环的mid就是等于l等于r,那么不管最后这个mid下标元素是大于目标值还是小于目标值都是返回l,因为在大于目标值时,r向前移动一位,l不动,此时r所在的位置元素一定是小于目标值的,而l下标位置的元素是大于目标值的,这时l就是符合插入条件的位置;小于目标值时l会向后移动一位,r不动,说明r位置元素小于目标值,l位置大于目标值,l符合,所以最后循环结束只要返回l就可以了。

收获:

从这到题的练习中进一步了解了二分的设计,在二分的几个难点上理解了二分查找的妙处,二分能最大的优化查找的时间复杂度,如果这个题目使用暴力解法,需要O(n)的时间复杂度,虽然O(n)的时间复杂度不算大,但是需要知道的是单纯的查找暴力解法也可行,但是在大多数考二分的题目中都不止是考察二分,可能会和其他的时间复杂度进行叠用,这时要是使用暴力,就会让时间复杂度大大增加,所以学好二分是为更难的算法做辅助。

34.在排序数组中查找元素的第一个和最后一个位置

文档讲解:代码随想录

状态:已做出

思路:

 这个题目从题目看来是查找,大概率是二分,在有序数组的情况下查找元素一般第一时间想到的就是二分。

看完题目后可以想到这个题目所考的点并不是查找某一个值,而是查找一个区间,这时使用的就不是单纯的二分了。根据题意,是要求我们找到目标值的区间,说明这个目标值在数组中可能重复出现,那么我们就需要进行两次二分,第一次二分找到符合的目标值所在的最小下标,第二次二分要找到最大的下标,这就是符合题目意思的答案。

代码如下:

class Solution {
public:
    vector searchRange(vector& nums, int target) {
        vectors(2,-1);
        if(nums.empty()) return s;
        int l=0;
        int r=nums.size()-1;
        while(l=target)
                r=mid;
            else l=mid+1;
        }
        if(nums[l]==target){
            s[0]=s[1]=l;
        }
        l=0;
        r=nums.size()-1;
        while(ltarget)
                r=mid-1;
            else l=mid;
        }
        if(nums[l]==target){
            s[1]=l;
        }
        return s;
    }
};

遇到的困难:

这个代码的难点就是需要掌握好二分的条件,两个二分循环条件都是l

最后一个难点就是在每个二分循环结束后要怎么去处理边界。第一个二分结束后需要判断结束后的下标是否等于目标值,因为我们的要求是找到大于等于目标值的最下下标,那么就有可能出现最后的下标是大于目标值的,所以需要判断。第二个二分循环也是同理。

收获:

通过练习此题目,深刻的体会了二分带来的便利,使用二分可以来求符合条件区间的最值,这样把一个问题分为两个小问题,分别求出符合要求的下标,最后就可以找到题目要求的目标值的区间了。

64.x的平方根

文档讲解: 代码随想录

状态:已做出

思路:

这个题目乍一看以为是使用math函数里的sqrt来求,但是题目却明确指出不能使用库函数来求解,这时就需要思考怎么去寻找平方根。但是题目指出找出不大于平方根的最大整数,这时就可以想到使用二分来求解题目。如果这个数的平方根正好是整数,就可以直接返回此时的mid,如果不是,那么就需要找到最接近这个数的平方根的数。

代码如下:

class Solution {
public:
    int mySqrt(int x) {
        long long l=0;
        long long r=x;
        while(lx) r=mid-1;
            else if(mid*midx) return l-1;
        else return l;
    }
};

遇到的困难:

这个题目最大的困难就是在二分查找平方根不是整数的情况下怎么处理循环结束后的l和r。在最后循环结束后说明这个平方根一定不是整数,那么此时l是等于r的,所以不管处理l还是r都是一样的,这时只要判断l或者r的平方是否大于给定的数就行了。大于的话就返回l-1或者r-1,小于就返回l或者r即可。

367.有效的完全平方数

文档讲解: 代码随想录

状态:已做出

思路:

思路和上一个题目完全一致,唯一不一样的就是最后循环结束后的处理不一样,因为题目要求是让我们判断这个给定的数的平方根是否为整数,只要在循环里出现mid平方等于给定的数就返回true,循环结束后因为l和r相等,所以只要随便判断l或者r的平方是否等于这个数,就可以返回符合要求的答案了。

代码如下:

class Solution {
public:
    bool isPerfectSquare(int num) {
        long long l=0;
        long long r=num;
        while(lnum) r=mid-1;
            else if(mid*mid

遇到的困难:

这个题目唯一的难点就是需要最循环结束后还需要判断l或者r平方是否等于给定的数,这样才能晚完全符合题意。

283.移动零

文档讲解:类似移除元素

状态:已做出

思路:

这个题目因为之前做过类似的,所以一看题目就知道应该去使用双指针来求解。

看完代码随想录文档给出的代码的思路后这个题目变得非常简单了,只需用到简单的几行代码就可以解决。

代码如下:

class Solution {
public:
    void moveZeroes(vector& nums) {
        int a=0;
        for(int i=0;i

遇到的困难:

这道题的难点就是如何控制双指针的递增,这个题目所用到的双指针就是快慢指针,两个指针在第二个指针的下标和零相等的时候同时增加,并且第二个指针的值要赋值给第一个指针的下元素,在第二个指针是零的情况下只有第二个指针进行递增。这样处理后循环结束后第一个指针的大小就是移除零后的数组大小,最后使用循环来便利这个范围的数组既可。

收获:

通过这个题目让我再次复习了双指针的使用方式,巩固了双指针的基础使用方法。

844.比较含退格符的字符串

文档讲解: 类似移除元素

状态:已做出

 思路:

这个题目与移除元素有点类似,但是也有很多不同。此题目需要分成两个循环分别求排除退格符后剩下的字符串的大小和所剩元素。使用双指针,针对于第一个循环的字符串,在第二个指针不等于’#‘的情况下,第二个指针下的元素赋值给第一个指针下的元素,在等于’#‘的情况下,第一个指针需要减一,因为#是退格符,但需要注意的是在第一个指针退到下标为0的时候就无法进行退格操作了。第一个指针还是用来保存退格操作后剩下字符串的长度,所以需要为两个字符串分别给出不同的双指针来进行之后的操作。

代码如下:

class Solution {
public:
    bool backspaceCompare(string s, string t) {
        int ssize=0;
        int tsize=0;
        for(int i=0;i0) ssize--;
        }
        for(int i=0;i0) tsize--;
        }
        if(ssize!=tsize) return false;
        for(int i=0;i

遇到的困难:

这个题目最大的难点就是要考虑怎么去处理退格符,只要遵循题目给出的退格符的作用就可以完成整个题目了。需要注意的是第一个指针退格后必要要大于等于0,不然会出现越界的运行错误,这是我当时出错的点。

收获:

通过练习这个题目,更加深层次地使用了双指针的用法,灵活的使用双指针就可以使整个题目变得非常简单。

977.有序数组的平方

文档讲解: 有序数组的平方

状态:已做出

思路:

这道题目我使用的是自定义排序做法,在class类中定义一个bool的静态成员函数,在这个函数中定义排序,使用绝对值来对数组进行排序,后面在题目给出的函数中利用sort加上自定义排序的bool函数来对需要处理的数组进行自定义排序,按照绝对值来进行从小到大的排序,排好后使用循环对每一个元素进行平方,最后返回这个数组既可。

代码如下:

class Solution {
public:
    static bool fun(int a,int b)
    {
        return abs(a) sortedSquares(vector& nums) {
        sort(nums.begin(),nums.end(),fun);
        for(int i=0;i

遇到的困难:

这个题目的困难就是如何去设计自定义排序的函数,只要函数设计好了后面的操作就非常简单,但是这个思路其实并不是最优解,时间复杂度还是过大,不过好在能通过。

收获:

通过这个题目又一次使用了自定义排序,使用自定义排序来求解代码量和思路都很好理解,最大的缺陷就是时间复杂度没有达到最优。

你可能感兴趣的:(算法,leetcode,数据结构)