代码随想录算法学习Day2| LeetCode977 有序数组的平方、LeetCode209 长度最小的子数组、LeetCode59 螺旋矩阵

 977.有序数组的平方

没看解题思路前:遍历数组,对每个元素平方后放入新的数组中,再对新数组做插入排序

public class Solution {
    public int[] SortedSquares(int[] nums) {
        int[] newNums = new int[nums.Length];
        for (int i = 0; i < nums.Length; i++)
        {
            newNums[i] = (int)Math.Pow(nums[i], 2);
            int temp = newNums[i];
            int j;
            for (j = i - 1; j >= 0; j--)
            {
                if (newNums[j] < temp)
                {
                    break;
                }
                else
                {
                    newNums[j + 1] = newNums[j];
                }
            }

            newNums[++j] = temp;
        }
        return newNums;
    }
}

看完解题思路后:双指针分别指向数组的首和尾,循环里每次比较两个指针所指向的数 的平方,大的元素放置到新数组中(从后往前),直到左指针大于右指针时循环结束,时间复杂度为O(n)

public class Solution
{
    public int[] SortedSquares(int[] nums)
    {
        int[] newNums = new int[nums.Length];
        int k = nums.Length - 1;
        for (int low = 0, high = nums.Length - 1; low <= high;)
        {
            int lowNum = (int)Math.Pow(nums[low], 2);
            int highNum = (int)Math.Pow(nums[high], 2);
            if (lowNum >= highNum)
            {
                newNums[k--] = lowNum;
                low++;
            }
            else
            {
                newNums[k--] = highNum;
                high--;
            }
        }

        return newNums;
    }
}

PS:知道这题是用双指针法,但是没有想到如何去用。并且本来打算暴力一点直接整个数组平方后再排序(可以用快速,这样时间复杂度只要O(nlogn),但是后来有另一个想法想试试,就是在每次平方后立马对新数组进行一次插入排序,结果时间复杂度更高O(n²)。

反思:没有想到如何去用双指针法,主要是因为没有观察到这组数的特点:有序数组中,平方后最大的数一定是在数组的两端,以后还是得多观察下规律。

209.长度最小的子数组

没看解题思路前:事先知道使用滑动窗口的方法,所以尝试带入:在循环中记录开始索引start,并不断计算总和total,直到total>=target的时候,记录结束索引end,并计算出本轮有多少个元素相加,下一轮再将开始索引start+1作为新的start开始计算,直到start等于数组最后一个索引时,停止循环。(这看着跟暴力解法没什么区别。。。看似是一个循环,实际也是两个循环,不同的是通过在一个循环中不停地修改i的值来实现的)

public class Solution
{
    public int MinSubArrayLen(int target, int[] nums)
    {
        int start = 0;
        int end = 0;
        int minLength = 0;
        int total = 0;
        for (int i = 0; i < nums.Length; i++)
        {
            if (total == 0) start = i;
            total += nums[i];
            if (total >= target)
            {
                end = i;
                int temp = end - start + 1;
                minLength = (temp < minLength || minLength == 0) ? temp : minLength;
                i = start;
                total = 0;
            }
        }

        return minLength;
    }
}

看完解题思路后:使用滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果

实现滑动窗口前需要明确的事:

  • 窗口内是什么?满足其和 ≥ target 的长度最小的 连续 子数组
  • 何时移动窗口的起始位置?如果当前窗口的值大于等于target了,说明窗口要向前移动了,也就是该缩小了,此时移动起始位置
  • 何时移动窗口的结束位置?窗口的结束位置就是遍历数组的索引,如果当前窗口的值小于target了,说明窗口该扩大了,此时移动结束位置
public class Solution
{
    public int MinSubArrayLen(int target, int[] nums)
    {
        int start = 0;
        int minLength = int.MaxValue;
        int total = 0;
        for (int end = 0; end < nums.Length; end++)
        {
            total += nums[end];
            while (total >= target)
            {
                minLength = Math.Min(end - start + 1, minLength);
                total -= nums[start];
                start++;
            }
        }

        return minLength == int.MaxValue ? 0 :minLength;
    }
}

反思:滑动窗口是在原来窗口的基础上进行移动(即减去起始位置的元素,更新原来的总和),而不是像我原先认为的只移动起始位置,然后重新计算窗口(即从起始位置开始,重新计算总和)

⭐️59.螺旋矩阵

没看解题思路前:在试图找规律,行i,列j,j先变化,再变化i,然后再变化j,再变化i,最后遍历到终点 [(n/2),(n-1)/2]时停止。但是在实现过程中越写越乱,边界搞不清,所以没实现出来。

看了解题思路后:本题不涉及任何算法,考察的是对代码的掌控能力(感觉挺主观的)

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

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

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

这四条边怎么画,画每条边时都要统一规则,坚持一致的左闭右开(或者左开右闭的原则),这样这一圈才能按照统一的规则画下来,如图:

public class Solution
{
    public int[][] GenerateMatrix(int n)
    {
        int[][] arr = new int[n][];
        for (int k = 0; k < n; k++)
            arr[k] = new int[n];

        int startX = 0, startY = 0;
        int num = 1;
        int i, j;
        int offset = 1;

        // 除2是因为:搜索一圈之后,下一圈的上边会往下走,下边会往上走,高度就少2,
        // 左边会往右走,右边会往左走,宽度就少2,每次下一圈都会比上一圈的高度宽度都少2
        int loop = n / 2; //循环几圈 
        while ((loop--) > 0)
        {
            i = startX;
            j = startY;
            // 下面开始的四个for就是模拟转了一圈(左闭右开)
            // 上行从左到右
            for (; j < n - offset; j++)
                arr[i][j] = num++;
            // 右列从上到下
            for (; i < n - offset; i++)
                arr[i][j] = num++;
            // 下行从右到左
            for (; j > startY; j--)
                arr[i][j] = num++;
            // 左列从下到上
            for (; i > startX; i--)
                arr[i][j] = num++;

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

            // offset 控制每一圈里每一条边遍历的长度,圈小了长度自然也变小了
            offset++;
        }

        // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值(最中间的位置可以说是最后一圈的第一个)
        if (n % 2 > 0)
            arr[startX][startY] = n * n;

        return arr;
    }
}

解题步骤:

  1. 初始化矩阵和起始位置
  2. 循环遍历每一圈,模拟填充上行、右列、下行、左列
  3. 控制每一圈里每一条边遍历的长度,起始位置的更新
  4. 单独处理奇数情况下矩阵中间位置的赋值
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值