算法纪实|Day2

发布时间 2023-07-13 20:13:33作者: 那一抹紫筠

数组02

LeetCode 997-有序数组的平方

有序数组的平方

思考

​ 第一眼的思路:先平方后排序,时间复杂度根据排序算法的选择决定,最快为O(nlgn),或者现根据绝对值排序后平方,本质上是一个思路。

​ 考虑双指针后的思路:存在负数的有序数组,平方后的最大值一定在两端,不考虑空间的消耗,从两端往中间遍历,比较两端点,大者存入新的数组,即可实现一轮循环完成排序,时间复杂度O(n),空间复杂度O(n)。

算法实现

​ 第一眼思路算法实现:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i = 0; i < nums.size(); i++){
            nums[i] *= nums[i];    
        }
        sort(nums.begin(), nums.end());
        return nums;
    }
};

​ 使用双指针之后的算法实现:

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> result(nums.size(), 0);//存放结果的数组
        int i = nums.size() - 1;
        int j = 0;
        int k = i;
        while(j <= i){
            int a = nums[i] * nums[i];
            int b = nums[j] * nums[j];
            if(a >= b){
                result[k--] = a;
                i--;
            }
            else{
                result[k--] = b;
                j++;
            }
        }
        return result;

    }
};
总结
  • ​ 新的双指针使用方式,首尾指针法,用于需要对数组进行收尾同时操作的过程中

  • ​ 顺序数组的良好性质,可方便的随机读取,实现头尾同时向中间遍历

LeetCode 209-长度最小的子数组

长度最小的子数组
思考

暴力解,使用两个for循环,依次寻找满足大于等于target的子串,并保留最小值,然后很自然的超时了

滑动窗口方法:

  • 设立两个指针,i、j
  • 当窗口内部元素,不满足条件时,i++,j不变
  • 当窗口内元素满足条件时,更新最短子串长度,并且j++,i不变
  • 重复上两过程

这样,时间复杂度就降低到O(n)啦

暴力解代码:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        //双循环
        int minSub = INT32_MAX;
        int i, j;
        int sum = 0;
        int sumLen = 0;
        for ( i = 0; i < nums.size(); i ++ ) {
            sum = 0;
            for ( j = i; j < nums.size(); j++ ) {
                sum += nums[j];
                if ( sum >= target ) {
                    sumLen = j - i + 1;
                    minSub = sumLen < minSub ? sumLen : minSub;
                    break;
                }
            }
        }
        return minSub == INT32_MAX ? 0 : minSub;
    }
};

滑动窗口代码:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int minSub = INT32_MAX;//最短子串长度
        int i, j;//双指针
        int sum = 0;//求和
        int sumLen = 0;//窗口长度
        
        for (i = 0, j = 0; j < nums.size(); j ++) {
            sum += nums[ j ];
            while ( sum >= target ) {
                sumLen = j - i + 1;
                minSub = minSub <= sumLen ? minSub : sumLen;
                sum -= nums[ i++ ];
            }
        }
        return minSub == INT32_MAX ? 0 : minSub;
    }
};
总结
  • 双指针方法的另一应用,前后指针都可移动,实现窗口的向后滑动
  • 这里使用wihle更好一些,需要循环加判断,或者使用if+for代替
  • 虽然有两层循环,但是我们对于整个数组的遍历只进行了i,j两遍,也就是O(2n)

LeetCode 59-螺旋矩阵II

螺旋矩阵II

思考

​ 第一眼:没有思路。

​ 看题解后:这是一道模拟过程的题目,那么最重要的就是分析好这个矩阵的填写过程,填写过程如下:

  • 从左往右
  • 从上到下
  • 从右往左
  • 从下到上

​ 上述过程就是填写一圈的流程,为了让这个流程可控,最好的方式是每条边填写的数字个数相同,考虑到循环不变量的原则,以左闭右开为区间定义,完成对四条边填写的控制;

​ 那么填写一圈的逻辑确定后,还需确定的是一共需要填写多少圈,每圈的起始位置在哪,每圈的边长为多少,对此我们需要初始化如下信息:

  • 每一圈的起始位置(0,0),(1,1)。。。
  • 需要循环的圈数,n/2,需要注意的是,n为奇数需要特殊处理最中间的数字,也就是最后一位数字的填写
  • 记录需要填写的数字count
  • 记录每圈的边长

那么实现代码如下:

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> matrix(n, vector<int>(n, 0));//初始化内容全部为0的二维数组
        int startx = 0, starty = 0;//每一圈的起点
        int loop = n / 2;//循环的圈数
        int length = n - 1;//定义每一圈的变边长,根据左闭右开,初始长度为n-1
        int count = 1;//每个位置应填的数值
        int i,j;//循环变量
        
        while ( loop-- ) {
            i = startx;
            j = starty;
            //开始模拟四个边的填写过程
            //写上边
            for ( ; j < length; j ++ ) {
                matrix[i][j] = count ++;
            }
            //写右边
            for ( ; i < length; i ++ ) {
                matrix[i][j] = count ++;
            }
            //写下边
            for ( ; j > starty; j -- ) {
                matrix[i][j] = count ++;
            }
            //写左边
            for ( ; i > startx; i --) {
                matrix[i][j] = count ++;
            }
            //下一圈,起点x++,y++,边长--
            startx ++;
            starty ++;
            length --;
        }

        //处理一下,n为奇数的情况
        if ( n % 2 ) {
            matrix[ n / 2 ][ n / 2 ] = count ++;
        }
        return matrix;
    }
};
总结
  • 过程模拟,重在考虑好各种情况,一个是分析四条边,一个是分析n的奇偶情况
  • 循环过程中,保持循环不变量原则,就不会出错