一、力扣题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;
}