算法训练 —— 数组(2)

 

目录

一、LeetCode977.有序数组的平方

二、LeetCode209.长度最小的子数组

三、LeetCode59.螺旋矩阵II


一、LeetCode977.有序数组的平方

有序数组的平方 

        首先,根据题目的意思,数组是递增序列,并且里面是含有负数的,要求我们将所有数字进行平方后,再次排列成一个有序数组。

解法1:

        暴力解法:在原数组遍历数组中的所有元素,并对其平方,赋值进去,然后进行排序即可;

 

class Solution {
public:
    void MergeSort(int left, int right, vector<int>& nums, vector<int>& tmp)
    {
        if(left >= right) return;
        int mid = (left + right) / 2;
        MergeSort(left, mid, nums, tmp);
        MergeSort(mid + 1, right, nums, tmp);
        int i = left, j = mid + 1;
        for(int k = left; k <= right; k++)
        {
            tmp[k] = nums[k];
        }
        for(int k = left; k <= right; k++)
        {
            if(i == mid + 1)
            {
                nums[k] = tmp[j++];
            }
            else if(j == right + 1 || tmp[i] < tmp[j])
            {
                nums[k] = tmp[i++];
            }
            else
            {
                nums[k] = tmp[j++];
            }
        }
    }
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> tmp(nums.size());
        for(int i = 0; i < nums.size(); i++)
        {
            nums[i] = nums[i] * nums[i];
        }
        //sort(nums.begin(),nums.end());//你可以使用C++的sort算法
        MergeSort(0, nums.size() - 1, nums, tmp);//也可以自己写一个排序算法,我这里写的是归并排序
        return nums;
    }
};

解法2:

        双指针法:我们从给出的数组中可以看出,数组被划分为了两个区间,左区间全部是负数,右区间全部是正数,并且数组是递增的,这样一来:对于数组的两端来说,平方之后都是最大值,从外向内一次减小,我们只需要将左右两端的值平方之后进行比较,取出最大的值,开辟一个新的数组,插入到数组的最后一个位置,然后从尾部迭代向前插入,就能得到有序数组的平方。

 

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n);
        int left = 0;
        int right = n - 1;
        int k = n - 1;
        while(left <= right)
        {    
            //最左边的平方值和最右边的平方值进行比较,谁大就把谁插入到res数组的最后面
            if(nums[left] * nums[left] > nums[right] * nums[right])
            {
                res[k--] =  nums[left] * nums[left];
                left++;
            }
            else
            {
                res[k--] =  nums[right] * nums[right];
                right--;
            }
        }
        return res;
    }
};

二、LeetCode209.长度最小的子数组

长度最小的子数组 

解法1:

        暴力解法:使用两层for循环,外循环主要用来遍历每个数组元素,内循环则是对每个元素进行向后累加,计算出长度最小的子数组,由于外循环会遍历每一个元素,所以内循环可以处理每个元素所对应的子数组长度,不断更新这个子数组长度,直到找到最小的。但是这道题,暴力解法会超时。但也是一种思路。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        if(n == 0) return 0;
        int minlength = INT_MAX;//INT_MAX整形的最大值,用来记录子数组长度
        for(int i = 0; i < n; i++)
        {
            int sum = 0;//每个元素计算完子数组长度后,都需要将这个sum置0,为了下一个元素重新计算
            for(int j = i; j < n; j++)
            {
                sum += nums[j];
                if(sum >= target)
                {
                    minlength = min(minlength, j - i + 1);
                    break;
                }
            }
        }
        return minlength == INT_MAX ? 0 : minlength;
    }
};

解法2:

        滑动窗口:所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。大致效果如下:

 

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int begin = 0; //滑动窗口的起始位置
        int minlength = INT_MAX;//记录最小的子序列
        int result = 0;//记录每一次子序列的长度
        int sum = 0;    //滑动窗口中数的和
        for(int end = 0; end < nums.size(); end++)//end作为滑动窗口的终止位置
        {
            sum += nums[end];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while(sum >= target)
            {
                result = end - begin + 1;//计算子序列
                minlength = min(minlength, result);//更新最小的子序列长度
                sum -= nums[begin++];//不断变更滑动窗口的起始位置,为了算出当前子序列中最小的子序列
            }
        }
        // 如果minlength没有被赋值的话,就返回0,说明没有符合条件的子序列
        return minlength == INT_MAX ? 0 : minlength;
    }
};

三、LeetCode59.螺旋矩阵II

螺旋矩阵II 

题解:

模拟顺时针画矩阵的过程:

  • 填充上行——从左到右
  • 填充右列——从上到下
  • 填充下行——从右到左
  • 填充左列——从下到上

由外向内一圈一圈这么画下去。

可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海;

        这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。

那么我按照左闭右开的原则,来画一圈,大家看一下:

  可以发现当n为奇数的时候,最后还剩下一个没有赋值,我们可以在最后做一下特殊处理。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n));
        int left = 0;
        int right = n - 1;
        int top = 0;
        int bottom = n - 1;
        int num = 1; //用来累加赋值
        int mid = n / 2;//如果n是奇数,就可以用mid处理最后一个值
        while(left < right && top < bottom)
        {
            //左->右
            for(int i = left; i < right; i++)
            {
                res[top][i] = num++;
            }
            //上->下
            for(int i = top; i < bottom; i++)
            {
                res[i][right] = num++;
            }
            
            //右->左
            for(int i = right; i > left; i--)
            {
                res[bottom][i] = num++;
            }
            //下->上
            for(int i = bottom; i > top; i--)
            {
                res[i][left] = num++;
            }
            //一圈结束后,将四个坐标更新到下一个圈
            left++;
            right--;
            top++;
            bottom--;
        }
        if(n % 2 == 1)//如果n是奇数,则循环结束后最中间的一个元素没有赋值,需要特殊处理
        {
            res[mid][mid] = num;
        }
        return res;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

霄沫凡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值