算法训练营day1 | 977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

 一、力扣题977. 有序数组的平方

题解

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

方法1:暴力破解

        直接遍历一遍数组将所有元素平方后再排序。

function sortedSquares($nums) {
        $result = [];
        for($i = 0; $i < count($nums); $i++) {
            $result[] = $nums[$i] ** 2;
        }
        sort($result);
        return $result;
    }

方法2:双指针法

        从题目可知数组nums为非递减数组,在数组中有负数和正数的情况下,平方后的数组中从两边到中间的元素应该是非递增的,也就是首尾的元素是最大的,所以可以将指针放在数组两端,平方后依次比较出最大值并往中间移动,拿到的元素就是从大到小的顺序,然后将这些元素反着放入数组中。

        经过试验后发现,php如果没有事先赋值,直接定义例如 $result[6] = 17,$result[0] = 5, 该数组中的第六个元素不会是17,第一个元素也不会是5,而是[17,5],也就是直接用数组下标来达到反着输入元素是不行的,需要事先给result数组的每个元素赋值。

function sortedSquares($nums) {
        $result = $nums;
        $i = 0;
        $j = $n = count($nums) - 1;
        while($i <= $j) {
            if($nums[$i] ** 2 >= $nums[$j] ** 2) {
                $result[$n--] = $nums[$i] ** 2;
                $i++;
            } else {
                $result[$n--] = $nums[$j] ** 2;
                $j--;
            }
        }
        return $result;
   }

二、力扣题209. 长度最小的子数组

题解

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

方法1:双重循环暴力破解

        设置 i 为子数组的初始位置, j 为终止位置, i 为外循环,j 为内循环,遍历得到 i 所在每一个位置的最小子数组,遍历过程中一一比较得到最小的子数组长度,时间复杂度为(O^2)

function minSubArrayLen($target, $nums) {
        $min = count($nums) + 1;
        for($i = 0; $i < count($nums); $i++) {
            $sum = 0;
            for($j = 0; $j < count($nums) - $i; $j++) {
                $sum += $nums[$i + $j];
                if($sum >= $target) {
                    $min = min($min, $j + 1);
                    break;
                }
            }
        }
        if($min > count($nums)) {
            return 0;
        }
        return $min;
    }

方法2:滑动窗口 

        这个方法也是使用双指针,上面的方法是每个外循环中,初始位置 i 固定,终止位置 j 一直移动来找到最小数组。而这个滑动窗口的方法,是 i 与 j 一起移动来降低时间复杂度。

        一开始 i 与 j 都为0, j 先向右移动直到 i 与 j 之间的子数组的和大于目标值,此时 i 会开始向右移动, 直到两者之间的子数组的和小于目标值, 然后 j 再继续向右移动。i 的配合可以使 j 省略移动的次数,故而将复杂度降为O(n)

function minSubArrayLen($target, $nums) {
        $sum = 0;
        $i = 0;
        $min = count($nums) + 1;
        for($j = 0; $j < count($nums); $j++) {
            $sum += $nums[$j];
            while($sum >= $target) {
                $min = min($min, $j - $i + 1);
                $sum -= $nums[$i];
                $i++;
            }
        }
        if($min > count($nums)) {
            return 0;
        }
        return $min;
    }

三、力扣题59. 螺旋矩阵 II

题解

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例 1:

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

示例 2:

输入:n = 1
输出:[[1]]

提示:

  • 1 <= n <= 20

方法:合理划分每次遍历的模块,坚持循环不变量原则

        我在做这道题时钻了牛角尖,热衷于研究排序时的 i 和 j的规律,假设(i,j)为该矩形的坐标,边长为n,那螺旋排序时,i与j的变化应为:j + (n-1) 【即 j +1 加了(n-1)次】, i + (n - 1), j - (n - 1), i - (n -2), j + (n-2), i + (n - 3), j - (n-3) ...... i-1, j+1.

        但这个规律要写成代码很难,有特别多要注意的地方,尝试了半个多小时后就放弃了,直接去看了视频怎么解,才豁然开朗原来要固定每道边的遍历长度。

        

        像这样,边长为3时,一个圈中的每次遍历占两格,四次遍历都相同长度了,就不会出现太多意外状况了。

        这个螺旋排序用到的变量很多,首先每转一个圈,初始位置都不同,故需设置初始i 和 j ;每次转圈遍历时的最后一个元素与边界的距离也不同,故需设置边界距离;还有循环时需用到的循环次数这个值。

         如果n为奇数,即中间最后会留下一个格空白,而每次循环完毕(i,j)都会回到每次循环的初始位置,将最后一次循环完毕的 i,j的值分别加1后就得到了中间格的坐标。

function generateMatrix($n) {
        if($n == 1) {               //n为1时是特殊情况,以下循环不适用
            return [[1]]
        }

        $matrix = [];
        for($i = 0; $i < $n; $i++) {        //数组初始化
            for($j = 0; $j < $n; $j++) {
                $matrix[$i][$j] = 0;
            }
        }

        $startx = 0;   //初始位置(x,y)
        $starty = 0;
        $o = 1;        //确定遍历的结点与边界的距离
        $num = 1;      //填写的值
        $a = intval($n / 2);  //用于控制循环圈数的值
        while($a > 0) {
            $i = $startx;
            $j = $starty;
            for(; $j < $n - $o; $j++) {
                $matrix[$i][$j] = $num++;
            }
            for(; $i < $n - $o; $i++) {
                $matrix[$i][$j] = $num++;
            }
            for(; $j > $starty; $j--) {
                $matrix[$i][$j] = $num++;
            }
            for(; $i > $startx; $i--) {
                $matrix[$i][$j] = $num++;
            }
            $o++;
            $startx++;
            $starty++;
            $a--;
        }
        if($n % 2 == 1) {
            $matrix[$i+1][$j+1] = $num;
        }
        return $matrix;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值