Leetcode 977.有序数组的平方
双指针(头尾两端指针值作比较)
思路:平方后最大数不是在左边就是在右边,所以可以引用双指针从数组两端开始比较,比较、并向中间移动对应的指针
问题: for(i=0,j=nums.Length-1;i<=j; )这个循环条件过了一段时间重刷直接乱写写了个while(i<=j)……就是注意这个循环条件+每次比较出头一个和最后一个中更大的那个存储到结果数组末端(因为要升序排列)
public class Solution {
public int[] SortedSquares(int[] nums) {
int[] result=new int[nums.Length];
int k=nums.Length-1;
int i,j;
for(i=0,j=nums.Length-1;i<=j; ){
if(nums[i]*nums[i]>nums[j]*nums[j]){
result[k]=nums[i]*nums[i];
k--;
i++;
}else{
result[k]=nums[j]*nums[j];
k--;
j--;
}
}
return result;
}
}
209.长度最小的子数组
示例一:暴力解法
暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。
public class Solution {
public int MinSubArrayLen(int target, int[] nums) {
int result = int.MaxValue; // 最终的结果
int sum = 0; // 子序列的数值之和
int subLength = 0; // 子序列的长度
for (int i = 0; i < nums.Length; i++) { // 设置子序列起点为i
sum = 0;
for (int j = i; j < nums.Length; j++) { // 设置子序列终止位置为j
sum += nums[j];
if (sum >= target) { // 一旦发现子序列和超过了s,更新result
subLength = j - i + 1; // 取子序列的长度
result = result < subLength ? result : subLength;
break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
}
}
}
// 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
return result == int.MaxValue ? 0 : result;
}
}
示例二:滑动窗口
滑动窗口(可以理解为特殊的双指针,窗口大小和起始位置会变化)
思路:滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。l两个指针,一个表示窗口起始位置、一个表示窗口终止位置。
窗口就是 满足其和 ≥ s 的长度最小的 连续 子数组。
窗口的起始位置如何移动:如果当前窗口的值大于等于s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
记忆:
1.最大整型值int.MaxValue。
2.for循环条件移动结束位置,循环体内移动开始位置。
3.移动开始位置前需要把原本开始位置的值减去再移动
public class Solution {
public int MinSubArrayLen(int target, int[] nums) {
int result=int.MaxValue;//初始化为最大值
int sum=0;
int subLen=0;
int i=0;
for(int j=0;j<nums.Length;j++){
sum+=nums[j];
while(sum>=target){
subLen=j-i+1;
result=result<subLen?result:subLen;//判断新的区间是不是比已知最小区间小,如果是的话就更新区间长度
sum-=nums[i];//需要移动滑动窗口左边指针了,所以相应的要把左边指针原本指向的值减去,再判断减去后条件是否仍然成立
i++;//移动左边指针
}
}
return result==int.MaxValue?0:result;//最后结果判断,如果得到了有效的结果值就返回结果值,否则返回0
}
}
Leetcode 59.螺旋矩阵II
思路:
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
由外向内一圈一圈这么画下去。
注意点:遍历条件,左右边界的开闭要一致,边界的值要清晰
以左闭右开为例
图片来源:代码随想录代码随想录 (programmercarl.com)
记忆:
1.C#二位数组初始化
-
int[][] answer = new int[n][];
:这一步创建了一个长度为n
的一维数组,每个元素都是一个整型数组的引用。但此时内部的每个整型数组并未初始化,它们仅仅是空引用。 -
answer[i] = new int[n];
:这个语句初始化了每个一维数组,并使其成为长度为n
的整型数组。通过循环遍历n
次,为每个一维数组分配了新的大小为n
的整型数组。
int[][] answer = new int[n][];
for(int i = 0; i < n; i++)
answer[i] = new int[n];
思路一:定义上下左右边界,根据图示的螺旋进行数值填充(螺旋矩阵I也可以用这种思路解)
public class Solution {
public int[][] GenerateMatrix(int n) {
//初始化
int[][] array=new int[n][];
for(int i=0;i<n;i++){
array[i]=new int[n];
}
//边界
int top=0;
int bottom=n-1;
int left=0;
int right=n-1;
int num=0;
while(num<n*n){
//从左到右
for(int i=left;i<=right;i++){
array[top][i]=++num;
}
top++;
//从上到下
for(int i=top;i<=bottom;i++){
array[i][right]=++num;
}
right--;
//从右到左
for(int i=right;i>=left;i--){
array[bottom][i]=++num;
}
bottom--;
//从下到上
for(int i=bottom;i>=top;i--){
array[i][left]=++num;
}
left++;
}
return array;
}
}
思路二:使用起始和结束边界值,在一圈赋值结束后缩小边界范围,并且单独处理n为奇数的情况。代码更简洁
public class Solution {
public int[][] GenerateMatrix(int n) {
// 初始化一个 n x n 的二维数组作为结果矩阵
int[][] answer = new int[n][]; // 创建一个长度为 n 的一维数组,每个元素是整型数组的引用
for (int i = 0; i < n; i++)
answer[i] = new int[n]; // 为每个一维数组分配一个长度为 n 的新整型数组
int start = 0; // 螺旋矩阵的起始行索引
int end = n - 1; // 螺旋矩阵的结束行索引
int tmp = 1; // 待填充的数值,从 1 开始递增
// 按顺时针螺旋顺序填充矩阵
while (tmp < n * n) {
// 填充上行
for (int i = start; i < end; i++)
answer[start][i] = tmp++;
// 填充右列
for (int i = start; i < end; i++)
answer[i][end] = tmp++;
// 填充下行
for (int i = end; i > start; i--)
answer[end][i] = tmp++;
// 填充左列
for (int i = end; i > start; i--)
answer[i][start] = tmp++;
start++;
end--;
}
// 处理奇数阶情况中心点
if (n % 2 == 1)
answer[n / 2][n / 2] = tmp;
return answer; // 返回生成的螺旋矩阵
}
}
两种思路的区别:
在第二种代码中的螺旋填充算法中,不需要额外考虑 n
是否为奇数的情况,因为该算法可以适用于任何正整数 n
。在循环过程中,通过调整边界并按照规定的顺序填充数字,最终能够正确生成螺旋排列的矩阵,无论 n
是奇数还是偶数。
在第二种算法中,由于我们是从外向内依次填充矩阵,最后会自然地填充到中心点,而不需要特殊处理。即使 n
为奇数,也能够正确地填充完整个矩阵。这种算法设计避免了在循环中针对奇数情况进行额外操作。
前一种方法需要考虑奇数和偶数情况,因为那种方法是基于设定了起始点和结束点,需要根据 n
的奇偶性来分别处理中心点的填充。而现在这种方法通过固定四个边界的方式,能够统一处理各种 n
的情况,使得中心点的填充变得更加简单且自然。