算法训练营day02|数组理论基础|977.有序数组的平方,209.长度最小的子数组,59螺旋矩阵II,54螺旋矩阵,48旋转图像

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.小结

双指针,滑动窗口,矩阵处理技巧

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值