代码随想录算法训练营第二天| 977.有序数组的平方、209.长度最小的子数组 59.螺旋矩阵ii C++

有序数组的平方

leetcode 977

调库

看到题目,最简单的思路就是直接排序,先将数组nums中的元素平方后,对nums进行排序

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> ivect;
        for (auto x: nums) {
            ivect.push_back(num * num);
        }
        sort(ivect.begin(), ivect.end());
        return ivect;
    }
};

算法的时间复杂度考虑STL库中排序方法,为O(nlogn),空间复杂度对排序来说为O(logn),对存储来说为O(n),考虑较大值,我认为是O(n)。

双指针,正序

在对数组中元素平方后,寻找数组中的最小值的下标索引smallest,创建一个新的vector ivect,长度与原数组相同,且ivect的第一个元素为平方数组numsqure索引为smallest的值,构建两个指针left和right,用于对平方数组的索引,left = smallest - 1,right = smallest + 1,以及一个cur用来指示应键入ivect数组的位置。

可用while和for循环来完成新数组元素的填入,本质都是数组的长度,针对for循环,可以考虑for(cur;cur<ivect,size();cur++),while循环考虑考虑(right - left < ivect.size() + 1)(此处加1的原因在于我对每次键入数据要对left--或right++,导致超出索引,以[0,4]的数组为例,最后left为-1,right为5),在循环体内,对numsqure[left]和numsqure[right]进行比较,较小值放在ivect[cur]的位置上,若放置为nums[left],则left--,若放置为nums[right],则right++,此外,索引cur++,同时,考虑两个特殊情况,当left == -1时,左边已经没有任何数据,可将right之后的数据完全放入数组,ivect[cur++] = numsqure[right++],当right == nums.size()-1时,右边已无任何数据,但考虑到左边数据为逆序,需反向放入数组中 ivect[cur++] = numsqure[left--];

循环结束,返回数组ivect。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        auto length = nums.size();
        vector<int>numsquare;
        for(auto x:nums){
            numsquare.push_back(x*x);
        }
        vector<int>ivect(length);
        int left =0;
        int smallest = 0;
        int right = 0;
        for(smallest; smallest< length-1;smallest++){
            if((numsquare[smallest] - numsquare[smallest+1])<0)
                break;
        }
        int cur{1};
        ivect[0] = numsquare[smallest];
        left = smallest - 1;
        right = smallest + 1;
        while((right - left)<length+1){
            if(right == length){
                ivect[cur++] = numsquare[left--];
                continue;
            }
            if(left == -1){
                ivect[cur++] = numsquare[right++];
                continue;
            }
            if(numsquare[left]<numsquare[right]){
                ivect[cur++] = numsquare[left--];
            }
            else{
                ivect[cur++] = numsquare[right++];
            }
        }
        return ivect;
        }
};

算法的时间复杂度应该为O(n),空间复杂度排序为O(1),存放答案的数组为O(n)。

写的比较差,周末有时间再改改,leetcode上消耗内存击败6.09%的C++用户,悲。

双指针法(参考代码随想录B站视频)

数组的平方考虑二次函数的曲线,在本题是两边大,中间小,创建双指针指向头尾,逆向更新数组,这种方法对边界的考虑更为简单。

利用for循环,创建i和j两个指针,分别指向数组的首和尾,注意for循环中判断的条件为i<=j,若为i<j,则会存在一个元素未排入新数组。由于i和j的更新取决于是否将i和j指向的元素放入新的数组,所以在for循环条件中将更新条件丢弃,在循环体内更新i和j。写法为: for(i,j;i<=j;){},在循环体内,用nums[i]*nums[i]和nums[j]*nums[j]进行大小比较,即可减少空间的使用,方法二的代码可以更新。

class Solution {
public:
    vector<int> sortedSquares(vector<int>& A) {
        int k = A.size() - 1;
        vector<int> result(A.size(), 0);
        for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素
            if (A[i] * A[i] < A[j] * A[j])  {
                result[k--] = A[j] * A[j];
                j--;
            }
            else {
                result[k--] = A[i] * A[i];
                i++;
            }
        }
        return result;
    }
};

长度最小的子数组

自己的思路(有错误)

长度最小的子数组,要找和大于等于target的长度最小的子数组,先看题中给定条件,首先,给定的数组和target均为正整数,若子数组存在,则长度最小的子数组,考虑到题目中数据量非常大,如果使用暴力求解,两次遍历一定会超时,所以考虑其他想法,想法一:长度最小的子数组应该包含数组中最大的数,所以可以考虑找到最大值的索引,在最大索引附近区域进行搜索,添加形成一个子数组。(错误的预设前提,考虑[4,6,1,8,1],target = 10)想法二:完整数组的和一定是最大的,i,j分别表示数组头和数组尾的索引,值为0和nums.size()-1,考虑从数组和中删去nums[i]和nums[j]中较小数值的索引,并更新i和j,并不断继续,当两侧都无法删除时,得到一个最接近的子数组,但我不能确认是否为长度最小的子数组。先按这个写,同时要先排除2个特殊情况,不存在和子数组长度为1的情况,开头需要遍历一边数组,并计算得到数组的和sumnum,若sumnum<target,直接return 0,此外,在遍历过程中,得到maxofnum,若maxofnum>=target,return 1。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        long long maxsum = 0;
        int maxofnum = 0;
        int left = 0;
        int right = nums.size()-1;//滑动窗口的索引 left和right,
        for(int i = 0; i < nums.size(); i++){
            maxsum += nums[i];
            if(nums[i]>maxofnum)
                maxofnum = nums[i];
        }
         if(maxsum<target)
            return 0;
         if(maxsum == target)
             return nums.size();
         if(maxofnum == target)
             return 1;
         int currentlength = nums.size();
         while(maxsum >= target){
             if(nums[left]<=nums[right]){
                 maxsum -= nums[left];
                 if(maxsum>=target){
                     left++;
                     currentlength--;
                 }
             }
             else{
                 maxsum -= nums[right];
                 if(maxsum>=target){
                     right--;
                     currentlength--;
                 }
             }
         }
            return currentlength;
    }
};

leetcode 测试用例19/21 也可以预见,这只是个局部最优,思路还是不对。时间复杂度O(n),空间复杂度O(1)。

滑动窗口法(参考代码随想录)

思考如果使用暴力求解的方法,则需要使用两个for循环,一个for循环找最优的初始位置,一个for循环找最优的终止位置,而在这种循环过程中,有很多冗余。

考虑2个指针left和right,left代表最优的初始位置,right代表最优的终止位置,也是循环的判断终点。

以right为循环条件,在循环体中对i进行自加或者不变,当j到达nums.size()时,循环结束

for(right;right<nums.size();right++)

left和right都从0开始,创建变量sum,在每次right的循环过程中,sum+=nums[right],此时,若sum大于等于target,则需计算此时的子长度,子长度为right - left + 1;并将其与全局的最短子长度进行比较,若小于全局的最短子长度,取而代之。且在这时,对left进行改变,sum -= nums[left],此时也应判断sum是否满足大于等于target,满足时则同样计算子长度并比较,直到不满足时,进行下一次的循环。具体的动画可以参考代码随想录官网的内容。

之后,判断我们得到的全局最短子长度是否合理,合理则返回,否则返回0。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int left = 0;
        int right = 0;//滑动窗口的索引 left和right,
        int minsubarraylen = INT_MAX;
        int temp = -1;
        int sum = 0;
        for(right;right<nums.size();right++){
            sum += nums[right];
            if(sum>=target){
                temp = right - left + 1;
                if (temp < minsubarraylen)
                {
                    minsubarraylen = temp;
                }
                while(sum >= target){
                        sum -= nums[left];
                        left++;
                        if(sum>= target) {
                            temp = right - left + 1;
                            if (temp < minsubarraylen)
                            {
                                minsubarraylen = temp;
                            }
                        }
                    }
                }
            }
            if(minsubarraylen>nums.size()+1){
            minsubarraylen = 0;
        }
        return minsubarraylen;
        }
    };

算法的时间复杂度为O(n),空间复杂度为O(1)

螺旋矩阵

直观思路:

创建一个n*n维的二维数组,vector<vector<int>> matrix(n, vector<int>(n, 0)),由于行和列都为n,一个循环从0到n^2,按顺时针放入矩阵中,同时需要注意的是,vector数组的顺序是从0到n-1,二维数组的赋值:matrix[row][column] = vals;

由于顺时针赋值,考虑四种模式,向右[0,0]->[0,max],向下[1,max]->[max,max],向左[max-1,max]->[min,n-1],向上[min, min+1],min和max为能存放的最大和最小的为0的区域,在向下结束后,max-=1;在向左完成后,min += 1;这种方式有一个问题,当n为2时,如果只是循环向下会越界,单独取出进行return

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n,vector<int>(n,0));//创建一个n*n维全0的二维数组
        int count = 1;
        int maxnum = n*n;
        int column = 0;
        int row = 0;
        int min = 0;
        int max = n - 1;
        int mode = 0;
        if(n == 2){
            matrix = {{1,2},{4,3}};
            return matrix;
        }
        while(count!=maxnum+1){
            if(mode%4==0){
                matrix[row][column] = count;
                count++;
                if(column == max){
                    row += 1;
                    mode++;
                    continue;
                }
                column++;
            }
            if(mode%4==1){
                matrix[row][column] = count;
                count++;
                if(row == max){
                    column -= 1;
                    mode++;
                    max -= 1;
                    continue;
                }
                row++;
            }
            if(mode%4==2){
                matrix[row][column] = count;
                count++;
                if(column == min){
                    row -= 1;
                    mode++;
                    min++;
                    continue;
                }
                column--;
            }
            if(mode%4==3){
                matrix[row][column] = count;
                count++;
                if(row == min){
                    column += 1;
                    mode++;
                    continue;
                }
                row--;
            }
        }

        return matrix;
    }
};

算法时间复杂度O(n^2),空间复杂度O(1);

  • 18
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的数组,使得数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值