1.知识点
1.1双指针
快慢指针:一快一慢
相向双指针:两个指针从数组两端往中间走
1.2滑动窗口
0.清楚定义窗口(比如窗口内的数之和大于100等)
1.先移动右边界形成一个符合要求的窗口
2.此时在符合窗口定义的基础上,尝试移动左边界尝试缩小窗口
1.3矩阵处理技巧
关于矩阵轨迹的题目,可以从宏观调度上考虑:
1.通过两个点坐标(左上角,右下角)可以确定一个矩阵,实现这个矩形要做的事情printEdge
2.同时移动两个点,收缩矩阵,循环调用printEdge,直到两个点在竖直方向或横向位置相交
3.实现printEdge函数的时候要考虑两个点在同一行或者同一列的情况
相关的题目LeetCode54,LeetCode59,LeetCode48
2.刷题
977.有序数组的平方
LeetCode链接 977. 有序数组的平方 - 力扣(LeetCode)
题目描述
方法1:双指针-相向双指针
package daimasuixiangshuati.day02_shuzu;
/**
* @Author LeiGe
* @Date 2023/9/23
* @Description todo
*/
public class YouXuShuZuDePingFang977_2 {
/**
* 方法1-双指针-相向双指针
* 0.根据数组非递减,数组中也可能有负数,那么平方后最大的数一定是在最前面或者最后面
* 因此,考虑从数组的最左边和最右边同时遍历,所得到的平方哪个大就选择哪个.
* 算法过程如下:
* 1.新建一个新数组result,长度大小和nums一样,用于保存平方结果,result从后往前填充
* 2.数组本来有序,负数平方后会变大,所以,平方后最大的数要么在数组开头,要么在数组结尾,比较两个结果,取最大的
* 2.1如果开头的大,取开头的平方,left指针右移,result数组指针前移
* 2.2如果后边的大,取结尾的平方,right指针前移,result数组指针前移
* left:数组开头
* right:数组结尾
* m:result数组的结尾
*
* @param nums
* @return
*/
public static int[] sortedSquares(int[] nums) {
//1.新建一个新数组,长度大小和nums一样,用于保存平方结果
int[] result = new int[nums.length];
int left = 0;
int right = nums.length - 1;
// result从后往前填充
int m = result.length - 1;
//2.数组本来有序,负数平方后会变大,所以,平方后最大的数要么在数组开头,要么在数组结尾,比较两个结果,取最大的
while (left <= right) {
if (nums[left] * nums[left] >= nums[right] * nums[right]) {
//2.1如果开头的大,取开头的平方,left指针右移,result数组指针前移
result[m] = nums[left] * nums[left];
m--;
left++;
} else {
//2.2如果后边的大,取结尾的平方,right指针前移,result数组指针前移
result[m] = nums[right] * nums[right];
m--;
right--;
}
}
return result;
}
}
时间复杂度O(N)
额外空间复杂度O(N)
209.长度最小的子数组
LeetCode链接 209. 长度最小的子数组 - 力扣(LeetCode)
题目描述
方法1:滑动窗口
package daimasuixiangshuati.day02_shuzu;
/**
* @Author LeiGe
* @Date 2023/9/23
* @Description todo
*/
public class ChangDuZuiXiaoDeZiShuZu209_2 {
/**
* 方法2-滑动窗口:就是不断的调节子序列的起始位置和终止位置,从而得出我们想要的结果,本质上上双指针
* 0.i:窗口开始位置,j:窗口结束位置
* 1.找到第一个满足sum>=target的位置,此时滑动窗口[i=0,j]
* 2.计算窗口长度,更新result
* 3.窗口开始位置右移,i++,sum-=nums[i],如果sum<target,则跳出while循环,j右移,直到sum又>=target.
*
* @param target
* @param nums
* @return
*/
public static int minSubArrayLen(int target, int[] nums) {
int length = nums.length;
int left = 0;
int sum = 0;
int subLen;
int minLen = Integer.MAX_VALUE;
for (int right = 0; right < length; right++) {
int num = nums[right];
sum += num;
// 找到第一个满足sum>=target的位置,此时滑动窗口[i=0,right]
// 尝试再满足条件的基础上,看能不能缩短长度,即左边右移
while (sum >= target) {
// 计算窗口长度
subLen = right - left + 1;
// 更新最小长度
minLen = Math.min(minLen, subLen);
// 左边界尝试往右移动
sum -= nums[left];
left++;
}
}
if (minLen == Integer.MAX_VALUE) {
return 0;
} else {
return minLen;
}
}
}
时间复杂度O(N)
空间复杂度O(1)
54.螺旋矩阵
LeetCode链接 54. 螺旋矩阵 - 力扣(LeetCode)
题目描述
方法1:矩形处理技巧
package daimasuixiangshuati.day02_shuzu;
import java.util.ArrayList;
import java.util.List;
/**
* @Author LeiGe
* @Date 2023/9/23
* @Description todo
*/
public class LuoXuanJuZhen54_2 {
/**
* 方法1:矩形处理技巧
* 通过两个点构成矩形的一个对角线(左上角和右下角两个点)
* 1.通过两个点确定一个矩形,矩形按照右->下->左->上的顺序遍历,实现处理矩形边框的函数addCircle即可
* 2.同时移动两个点,收缩矩形,循环调用addCircle,直到两点在竖方向或横向相交
*
* @param matrix
* @return
*/
public List<Integer> spiralOrder(int[][] matrix) {
ArrayList<Integer> list = new ArrayList<>();
// 最开始的那个圈的左上角的坐标和右下脚的坐标
int leftRow = 0;
int leftCol = 0;
int rightRow = matrix.length - 1;
int rightCol = matrix[0].length - 1;
// 左上角的点往右下角移动, 右下角的点往左上角移动,直到两个坐标相遇
while (leftRow <= rightRow && leftCol <= rightCol) {
addCircle(list, matrix, leftRow, leftCol, rightRow, rightCol);
leftRow++;
leftCol++;
rightRow--;
rightCol--;
}
return list;
}
/**
* 遍历矩形的顺序:右->下->左->上
*
* @param list
* @param matrix
* @param lRow 左上角横坐标
* @param lCol 左上角纵坐标
* @param rRow 右下角横坐标
* @param rCol 右下角纵坐标
*/
private void addCircle(ArrayList<Integer> list, int[][] matrix, int lRow, int lCol, int rRow, int rCol) {
// 如果两个点在一条横线上,从左往右打印横线
if (lRow == rRow) {
for (int i = lRow; i <= rCol; i++) {
list.add(matrix[lRow][i]);
}
}
// 如果两个点在一条竖线线上,从上往下打印竖线
else if (lCol == rCol) {
for (int i = lRow; i <= rRow; i++) {
list.add(matrix[i][lCol]);
}
}
// 两个点可以形成矩形,右->下->左->上打印四个边
else {
int row = lRow;
int col = lCol;
//往右
while (col != rCol) {
list.add(matrix[lRow][col]);
col++;
}
// 往下
while (row != rRow) {
list.add(matrix[row][rCol]);
row++;
}
// 往左
while (col != lCol) {
list.add(matrix[rRow][col]);
col--;
}
// 往上
while (row != lRow) {
list.add(matrix[row][lCol]);
row--;
}
}
}
}
时间复杂度O(N*M)
空间复杂度O(1)
59.螺旋矩阵II
LeetCode链接 59. 螺旋矩阵 II - 力扣(LeetCode)
题目描述
方法1:矩形处理技巧
package daimasuixiangshuati.day02_shuzu;
/**
* @Author LeiGe
* @Date 2023/9/23
* @Description todo
*/
public class LuoXuanJuZhen59_2 {
/**
* 方法1:通过两个点构成矩形的一个对角线(左上角和右下角两个点)
* 1.通过两个点确定一个矩形,矩形按照右->下->左->上的顺序遍历
* 2.同时移动两个点,收缩矩形
*
* @param n
* @return
*/
int cur = 1;
public int[][] generateMatrix(int n) {
int[][] matrix = new int[n][n];
int cur = 1;
// 最开始的那个圈的左上角的坐标和右下脚的坐标
int leftRow = 0;
int leftCol = 0;
int rightRow = matrix.length - 1;
int rightCol = matrix[0].length - 1;
// 左上角的点往右下角移动, 右下角的点往左上角移动,直到两个坐标相遇
while (leftRow <= rightRow && leftCol <= rightCol) {
addCircle(matrix, leftRow, leftCol, rightRow, rightCol);
leftRow++;
leftCol++;
rightRow--;
rightCol--;
}
return matrix;
}
/**
* @param matrix
* @param lRow 左上角横坐标
* @param lCol 左上角纵坐标
* @param rRow 右下角横坐标
* @param rCol 右下角纵坐标
*/
private void addCircle(int[][] matrix, int lRow, int lCol, int rRow, int rCol) {
// 如果两个点在一条横线上,从左往右打印横线
if (lRow == rRow) {
for (int i = lRow; i <= rCol; i++) {
matrix[lRow][i] = cur++;
}
}
// 如果两个点在一条竖线线上,从上往下打印竖线
else if (lCol == rCol) {
for (int i = lRow; i <= rRow; i++) {
matrix[i][lCol] = cur++;
}
}
// 两个点可以形成矩形,右->下->左->上打印四个边
else {
int row = lRow;
int col = lCol;
//往右
while (col != rCol) {
matrix[lRow][col] = cur++;
col++;
}
// 往下
while (row != rRow) {
matrix[row][rCol] = cur++;
row++;
}
// 往左
while (col != lCol) {
matrix[rRow][col] = cur++;
col--;
}
// 往上
while (row != lRow) {
matrix[row][lCol] = cur++;
row--;
}
}
}
}
时间复杂度O(N^2)
48.旋转图像
LeetCode连接 48. 旋转图像 - 力扣(LeetCode)
题目描述
方法1:矩阵处理技巧
package daimasuixiangshuati.day02_shuzu;
/**
* @Author LeiGe
* @Date 2023/9/23
* @Description todo
*/
public class XuanZhuanTuXiang48_2 {
/**
* 方法1:通过两个点构成矩形的一个对角线(左上角和右下角两个点)
* 1.通过 两个点确定一个矩形,将这个矩形上边的值旋转90度,实现rotateCircle函数(将每条边上的数分为rCol-lCol组)
* 2.同时移动两个点,收缩矩形,循环调用rotateCircle,直到两点在竖方向或横向相交
*
* @param matrix
*/
public void rotate(int[][] matrix) {
//开始:左上角的点
int leftRow = 0;
int leftCol = 0;
//开始:右下角的点
int rightRow = matrix.length - 1;
int rightCol = matrix[0].length - 1;
//左上角的点往右下角移,右下角的点往左上角移动,旋转矩形
while (leftRow <= rightRow && leftCol <= rightCol) {
rotateCircle(matrix, leftRow++, leftCol++, rightRow--, rightCol--);
}
}
/**
* 旋转矩形
*
* @param matrix 数组
* @param lRow 左上角点的横坐标
* @param lCol 左上角点的纵坐标
* @param rRow 右下角点的横坐标
* @param rCol 右下角点的纵坐标
*/
private void rotateCircle(int[][] matrix, int lRow, int lCol, int rRow, int rCol) {
int tmp;
// 将一条边上的数可以分成rCol-lCol组,矩形按照上,右,下,左的顺序将4条边编号为1,2,3,4
// 交换每组数在4条边的位置,比如:
// 第4条边的第1组位置的数去第1条边的第1组位置
// 第1条边的第1组位置的数去第2条边的第1组位置
// 第2条边的第1组位置的数去第3条边的第1组位置
// 第3条边的第1组位置的数去第4条边的第1组位置
// i表示组号,每组交换4条边的数
for (int i = 0; i < rCol - lCol; i++) {
// 记录第1条边的数
tmp = matrix[lRow][lCol + i];
// 第1条边的数来自第4条边
matrix[lRow][lCol + i] = matrix[rRow - i][lCol];
// 第4条边的数来自第3条边
matrix[rRow - i][lCol] = matrix[rRow][rCol - i];
// 第3条边的数来自第2条边
matrix[rRow][rCol - i] = matrix[lRow + i][rCol];
// 第2条边的数来自第1条边
matrix[lRow + i][rCol] = tmp;
}
}
}
3.小结
双指针,滑动窗口,矩阵处理技巧