代码随想录


)


前言

今天完成的是对数组内容部分的首尾。前两道题目吗仍然涉及到双指针算法,第三道题目则是运用到二维前缀和部分的相关知识。


一、LeetCode977.有序数组的平方

题目解析:给大家一个有序数组,然后返回一个所有元素平方后返回的一个数组,要求该数组依然有序。(原数组包含复数)暴力思路:可以先所有平方,然后在进行排序(可以快速排序,归并排序)。双指针思路:最大的元素一定从原数组的两边开始取到。则设计两个指针分别从首尾两端向中间靠拢。靠拢时比较指向的数的平方的大小。根据题目的要求。我们选择那个较大的或者较小的,并且将指针代表的下标进行移动。指针不断移动的过程就是循环的过程。循环要求的条件check().则是例如的i < j 等到条件(相遇的时候就会终止)。双指针思路的重要一环:寻找i++和j++的条件。

JAVA代码如下(已经添加注释)

class Solution {
    public int[] sortedSquares(int[] nums) {
        int n=nums.length;
        int[] ans=new int[n];//最后返回的新数组
        int start=0;//开头的变量,可以看成i
        int last=n-1;//结尾的变量,可以看成j
        while (start<=last){//相遇的时候终止循环
            n--;//代表新数组的元素的下标,我们要求从下到大,那么就到着写
            //接下来的是具体的移动过程,先比较大小,看条件选择一个进行移动
            if (nums[start]*nums[start]>nums[last]*nums[last]){
                ans[n]=nums[start]*nums[start];
                start++;
            }else {
                ans[n]=nums[last]*nums[last];
                last--;
            }
        }
        return ans;
    }
}

C++代码如下:(含注释)

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;) { // 定义变量和终止条件
        //进行移动
            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;
    }
};

简单提一下:个人还是比较习惯实用while循环来写,因为终止条件可以看的更加明显。

二、LeetCode 209.长度最小的子数组

题目解析:看到题目的第一印象,遍历出数组的所有的情况,让后进行比大小。比大小还好说,就是可以每一次一一的进行比较,但是遍历数组的情况时,我们的反应就是两层for循环,一层的循环变量用来记录数组的起始位置,另一层循环遍历用来记录数组的结束为止。诶!!这里就注意了,记录位置,那我们不就可以想到指针嘛,而它恰好又是双层循环,那么我们不是正好就可以尝试一下双指针嘛。一个for循环来做两个for循环来做的事情。那么循环里的索引下标j表示的是起始位置还是初始位置呢?我们假设它是起始位置,那么另一个坐标需要遍历数组中在它之后的所有元素。会比较麻烦(和暴力就没有什么区别了)。所以for循环里的 j 表示的是截止位置。所以我们来探索起始位置就好了。随着终止位置不断移动,如果这次形成的这个集合符合条件的话,那么就记录一下此时的长度并且继续尝试下一次的移动。
所以核心且重点的代码!!

while(sum >= s){
            sublength = j-i+1;//取得子序列的长度
            result = result <sublength ? result : sublength;//更新此时的长度
            sum -= nums[i++];//这里是滑动窗口的精妙支出,不断表更i(子序列的起始位置)
        }

本题目JAVA代码如下

class Solution {
        // 滑动窗口
    public int minSubArrayLen(int s, int[] nums) {
        int left = 0;
        int sum = 0;
        int result = Integer.MAX_VALUE;
        for (int right = 0; right < nums.length; right++) {//末位置移动。
            sum += nums[right];//题目要求的区间和
            //核心代码块
            while (sum >= s) {
                result = Math.min(result, right - left + 1);//取得子序列的长度并且更新此时的长度
                sum -= nums[left++];//这里是滑动窗口的精妙支出,不断表更i(子序列的起始位置)
            }
        }
        return result == Integer.MAX_VALUE ? 0 : result;
    }
}

补充题目: LeetCode 799.最长的包含不重复元素的子数列

暴力算法:

for(int i = 0.;j < n ;j++){
    for(int j = 0; j <= i;j++){
        if check(i,j){
            res = min(res,i - j +1)
        }
    }
}

双指针做法:

i每一次移动一个单位,在求出每个i会对应一个j,只枚举遍历 i 和 j 的话只需要判断需不需要再次移动。

for(int i = 0,j = 0;i <n;i++)
{
    while(j <= i && check(i,j)) j++
    res = max(res,j - i + 1);
}

C++代码答案

#include <iostream>

using namespace std;

const int N = 100010;

int n;
int q[N], s[N];

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

    int res = 0;
    for (int i = 0, j = 0; i < n; i ++ )
    {
        s[q[i]] ++ ;
        while (j < i && s[q[i]] > 1) s[q[j ++ ]] -- ;
        res = max(res, i - j + 1);
    }

    cout << res << endl;

    return 0;
}


三、LeetCode 59.螺旋矩阵

题目解析:本题是一个模拟转圈的过程,某种角度来看,它和二维的前缀和是及其相似的。前缀和的概念大家可以类比高等数学中的级数和的概念。
在讨论本题目之前,我先去介绍一下一道求子矩阵的和的LeetCode题目,题号为796.
在这里插入图片描述

大矩阵的前缀和等于红色矩阵的前缀和加上绿色矩阵的前缀和(有点像容斥原理),并且继续递归下去。

C++代码如下

#include <iostream>

using namespace std;

const int N = 1010;

int n, m, q;
int s[N][N];

int main()
{
    scanf("%d%d%d", &n, &m, &q);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[i][j]);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

    while (q -- )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]);
    }

    return 0;
}

那么本题目可以类比

JAVA版本

class Solution {
    public int[][] generateMatrix(int n) {
        int loop = 0;  // 控制循环次数
        int[][] res = new int[n][n];
        int start = 0;  // 每次循环的开始点(start, start)
        int count = 1;  // 定义填充数字
        int i, j;

        while (loop++ < n / 2) { // 判断边界后,loop从1开始
            // 模拟上侧从左到右
            for (j = start; j < n - loop; j++) {
                res[start][j] = count++;
            }

            // 模拟右侧从上到下
            for (i = start; i < n - loop; i++) {
                res[i][j] = count++;
            }

            // 模拟下侧从右到左
            for (; j >= loop; j--) {
                res[i][j] = count++;
            }

            // 模拟左侧从下到上
            for (; i >= loop; i--) {
                res[i][j] = count++;
            }
            start++;
        }

        if (n % 2 == 1) {
            res[start][start] = count;
        }

        return res;
    }
}



C++版本

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
        int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
        int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
        int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
        int count = 1; // 用来给矩阵中每一个空格赋值
        int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
        int i,j;
        while (loop --) {
            i = startx;
            j = starty;

            // 下面开始的四个for就是模拟转了一圈
            // 模拟填充上行从左到右(左闭右开)
            for (j = starty; j < n - offset; j++) {
                res[startx][j] = count++;
            }
            // 模拟填充右列从上到下(左闭右开)
            for (i = startx; i < n - offset; i++) {
                res[i][j] = count++;
            }
            // 模拟填充下行从右到左(左闭右开)
            for (; j > starty; j--) {
                res[i][j] = count++;
            }
            // 模拟填充左列从下到上(左闭右开)
            for (; i > startx; i--) {
                res[i][j] = count++;
            }

            // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
            startx++;
            starty++;

            // offset 控制每一圈里每一条边遍历的长度
            offset += 1;
        }

        // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
        if (n % 2) {
            res[mid][mid] = count;
        }
        return res;
    }
};

四、总结

今天刷的题目总结了很多,不过很多还没有完全理解下来,还需要再巩固一下。还是有些力不从心,不过状态也好多了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值