3.数组(三)

2020-04-03  本文已影响0人  今天柚稚了么

https://leetcode-cn.com/tag/array/

题目汇总

55. 跳跃游戏中等※

56. 合并区间中等※

57. 插入区间困难※※

59. 螺旋矩阵 II中等

62. 不同路径中等

63. 不同路径 II中等

64. 最小路径和中等

66. 加一简单※

73. 矩阵置零中等

74. 搜索二维矩阵中等

55. 跳跃游戏中等※

给定一个非负整数数组,你最初位于数组的第一个位置。数组中的每个元素代表你在该位置可以跳跃的最大长度。判断你是否能够到达最后一个位置。
输入: [2,3,1,1,4],输出: true
解释: 我们可以先跳 1 步,从位置 0 到达位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
输入: [3,2,1,0,4],输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置

思路:

出处:这么简洁的算法,膜拜大佬!
如果某一个作为起跳点的格子可以跳跃的距离是 3,那么表示后面 3 个格子都可以作为起跳点。
可以对每一个能作为起跳点的格子都尝试跳一次,把能跳到最远的距离不断更新。
如果可以一直跳到最后,就成功了。

class Solution {
    public boolean canJump(int[] nums) {
        int k = 0; //k是当前能跳到的最远距离,每个位置都更新k,
        for (int i = 0; i < nums.length; i++)
        {
            //如果到一个位置发现 i > k
            //也就是说现在这个位置 i 已经超过了之前能跳到的最远距离,就返回false,否则返回true
            if (i > k) 
                return false;
            //i + nums[i]代表站在索引i的格子上,并且可以从这个格子能跳到最远的距离(nums[i])
            //这两个数值相比,较大的就可以继续保存成 k ,代表可以跳到最远的地方
            k = Math.max(k, i + nums[i]);
        }
        return true;
    }
}

56. 合并区间中等※

给出一个区间的集合,请合并所有重叠的区间。
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6]

思路:排序,时间复杂度O(nlogn)

官方题解第二种
首先,将输入的区间集合排好序

然后,我们将第一个区间插入 merged 数组中,然后按顺序考虑之后的每个区间:如果当前区间的左端点在前一个区间的右端点之后,那么他们不会重合,我们可以直接将这个区间插入 merged 中;否则,他们重合,我们用当前区间的右端点更新前一个区间的右端点 end,如果前者数值比后者大的话

class Solution {
    private static class Interval {
        int start;
        int end;
        Interval(int[] interval) {
            this.start = interval[0];
            this.end = interval[1];
        }

        int[] toArray() {
            return new int[]{this.start, this.end};
        }
    }
    private class IntervalComparator implements Comparator<Interval>{
        @Override
        public int compare(Interval a, Interval b){
            //return a.start < b.start ? -1 : a.start == b.start ? 0 : 1;
            return a.start - b.start;
        }            
    }
    public int[][] merge(int[][] intervals) {
        List<Interval> intervalsList = new LinkedList<>();
        for (int[] interval : intervals) {
            intervalsList.add(new Interval(interval));
        }
        //先将输入的区间集合排好序
        intervalsList.sort(new IntervalComparator());
        LinkedList<Interval> merged = new LinkedList<>();
        for (Interval interval : intervalsList) {
            if (merged.isEmpty() || merged.getLast().end < interval.start) {
                merged.add(interval);//区间不用合并
            }

            else {//区间合并
                merged.getLast().end = Math.max(merged.getLast().end, interval.end);
            }
        }

        int i = 0;
        int[][] result = new int[merged.size()][2];
        for (Interval mergedInterval : merged) {
            result[i] = mergedInterval.toArray();
            i++;
        }
        return result;

    }
}

57. 插入区间困难※※

给出一个无重叠的,按照区间起始端点排序的区间列表。
在列表中插入一个新的区间,你需要确保列表中的区间仍然有序且不重叠(如果有必要的话,可以合并区间)。
输入: intervals = [[1,3],[6,9]], newInterval = [2,5]
输出: [[1,5],[6,9]]

思路:贪心算法

见官方题解

59. 螺旋矩阵 II中等

给定一个正整数 n,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
输入: 3
输出:
[
[ 1, 2, 3 ],
[ 8, 9, 4 ],
[ 7, 6, 5 ]
]

54. 螺旋矩阵类似

思路:定义边界

定义上下左右四个边界值,始终按照从左到右、从上到下、从右到左、从下到上的填入顺序循环

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];
        int left = 0;
        int right = n - 1;
        int up = 0;
        int down = n - 1;
        int num = 1, end = n * n;
        while(num <= end){
            //从左到右,最上边一行,向右走存入整行的值
            for(int col=left;col<=right;col++)
                matrix[up][col]=num++;          
            up++;//向下逼近

            //从上到下,最右边一列,向下走存入整列的值
            for(int row=up;row<=down;row++)
                matrix[row][right]=num++;
            right--;//向左逼近

            //从右到左,最下边一行,向左走存入整行的值
            for(int col=right;col>=left;col--)
                matrix[down][col]=num++;           
            down--;//向上逼近

            //从下到上,最左边一列,向上走存入整列的值
            for(int row=down;row>=up;row--)
                matrix[row][left]=num++;
            left++;//向右逼近
        }
        return matrix;
    }
}

62. 不同路径中等

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。问总共有多少条不同的路径?


输入: m = 7, n = 3,输出: 28
思路:动态规划

dp[i][j]的值是从起始点走到(i, j)的路径数。由于机器人每次只能向下或者向右移动一步,dp[i][j]的值就是第 i 行第 j 列这个格子的上面格子的值加上左边格子的值,也就是dp[i][j] = dp[i-1][j] + dp[i][j-1],其中第一行 dp[0][j]=0,第一列 dp[i][0]=0

class Solution {
    public int uniquePaths(int m, int n) {
        if(m < 0 || n < 0)
            return 0;
        if(m == 0 || n == 0)
            return 1;
        int[][] dp = new int[m][n];
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){ 
                if (i == 0 || j == 0) {     //最上一行或者最左一列
                    dp[i][j] = 1;
                }
                else{
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
                }        
            }
        }
    return dp[m-1][n-1];
    }
}

63. 不同路径 II中等

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?


网格中的障碍物和空位置分别用 10 来表示。
说明:m 和 n 的值均不超过 100。
输入:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
输出: 2
3x3 网格的正中间有一个障碍物。从左上角到右下角一共有 2 条不同的路径
  1. 向右 -> 向右 -> 向下 -> 向下
  2. 向下 -> 向下 -> 向右 -> 向右
思路:动态规划

62. 不同路径相比只是增加了障碍物

class Solution {
    public int uniquePathsWithObstacles(int[][] obstacleGrid) {
        if (obstacleGrid == null || obstacleGrid[0].length == 0) {
            return 0;
        }
        int row = obstacleGrid.length;//行数
        int col = obstacleGrid[0].length;//列数
        int[][] dp = new int[row][col];
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                if(obstacleGrid[i][j]==1){
                    dp[i][j] = 0;
                }else{
                    if(i==0 && j==0) dp[i][j]=1;
                    else if(i==0) dp[i][j]=dp[i][j-1];//第一行
                    else if(j==0) dp[i][j]=dp[i-1][j];//第一列
                    else dp[i][j]=dp[i-1][j]+dp[i][j-1];
                }
            }
            
        }
    return dp[row-1][col-1];
    }
}

64. 最小路径和中等

给定一个包含非负整数的 m x n 网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。
说明:每次只能向下或者向右移动一步。
输入:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

思路:动态规划

分为四种情况
(1)当i = 0, j = 0时,其实就是起点
(2)当j=0时,grid[i][j] = grid[i-1][j] + grid[i][j]
(3)当i=0时,grid[i][j] = grid[i][j-1]+ grid[i][j]
(4)当i,j均不为0时,grid[i][j] = Math.min(grid[i-1][j], grid[i][j-1])+ grid[i][j]

class Solution {
    public int minPathSum(int[][] grid) {
        int row = grid.length;
        int col = grid[0].length;
        for(int i=0;i<row;i++){
            for(int j=0;j<col;j++){
                if(i==0&&j==0)
                    continue;
                else if(i==0){
                    grid[i][j] += grid[i][j-1];
                }
                else if(j==0){
                    grid[i][j] +=  grid[i-1][j];
                }  
                else{
                    grid[i][j] += Math.min(grid[i-1][j], grid[i][j-1]);
                }
            }
        }
       
        return grid[row-1][col-1];
    }
}

66. 加一简单※

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。

思路:

只要+1求余数,余数不等于0,说明没有进位,直接返回。如果余数等于0,说明有进位,遍历前一个数字,加1再求余数,以此循环。如果for循环都遍历完了,说明最高位也有进位,则重新建立一个数组,初始化为0,将第一位设置为1就可以了,因为,99、999之类的数字加一为100、1000。
这个精选题解很巧妙,太强了,代码简洁易懂

class Solution {
    public int[] plusOne(int[] digits) {
        int len = digits.length;
        for(int i=len-1;i>=0;i--){
            digits[i]++;
            digits[i]%=10;
            if(digits[i]!=0) 
                return digits;
        }
        digits = new int[len + 1];
        digits[0] = 1;
        return digits;
    }
}

73. 矩阵置零中等

给定一个 m x n 的矩阵,如果一个元素为 0,则将其所在行和列的所有元素都设为 0。请使用原地算法。
输入:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
输出:
[
[1,0,1],
[0,0,0],
[1,0,1]
]

思路:

遍历数组,找到矩阵中元素为0时它的行号和列号,定义boolean变量,这些行和列的所有元素在下一次遍历时全部赋为零。

class Solution {
    public void setZeroes(int[][] matrix) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        boolean[] judgeRow=new boolean[rows];
        boolean[] judgeCol=new boolean[cols];
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if(matrix[i][j]==0){
                    judgeRow[i]=true;
                    judgeCol[j]=true;         
                }
            }       
        }
        
        for(int i=0;i<rows;i++){
            for(int j=0;j<cols;j++){
                if (judgeRow[i]||judgeCol[j]) {
                    matrix[i][j]=0;
                }
            }       
        }        
       
    }
}

74. 搜索二维矩阵中等

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
输出: true

思路一:从右上角开始找

首先选取数组中右上角的数字。如果该数字等于要查找的数字,则查找过程结束;如果该数字大于要查找的数字,则剔除这个数字所在的列;如果该数字小于要查找的数字,则剔除这个数字所在的行。如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除一行或者一列,逐步缩小范围

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int rows = matrix.length;
        if(rows == 0)
            return false;
        int cols = matrix[0].length;
        if(cols == 0)
            return false;
        //从右上角开始找
        int row = 0;
        int col = cols - 1;
        while(row<rows&&col>=0){
            if(matrix[row][col]>target){
                col--;
            }else if(matrix[row][col]<target){
                row++;
            }else{
                return true;
            }
        }
    return false;
    }
}
思路二:二分查找

该矩阵可以看成一个有序数组,数组中含有行数×列数个元素,虚数组的序号可以转化为原矩阵中的行和列,这样自然而然想到二分查找

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int rows = matrix.length;
        if(rows == 0)
            return false;
        int cols = matrix[0].length;
        if(cols == 0)
            return false;
        int nums = rows * cols;
        int left = 0;
        int right = nums - 1;
        while(left <= right){
            int mid = (right - left)/2 + left;
            if(matrix[mid/cols][mid%cols] > target){
                right = mid - 1;
            }else if(matrix[mid/cols][mid%cols] < target){
                left = mid + 1;
            }else{
                return true;
            }

        }
    return false;
    }
}
上一篇下一篇

猜你喜欢

热点阅读