Letbook Cookbook题单——数组4

28 篇文章 0 订阅
10 篇文章 0 订阅

Letbook Cookbook题单——数组4

59. 螺旋矩阵 II

难度中等

给你一个正整数 n ,生成一个包含 1n^2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FnOybsod-1670475221171)(https://assets.leetcode.com/uploads/2020/11/13/spiraln.jpg)]

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

示例 2:

输入:n = 1
输出:[[1]]

提示:

  • 1 <= n <= 20

构造一个螺旋矩阵,迭代模拟和递归模拟都可以

迭代

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<int>nums(n*n);
        for(int i=0;i<n*n;i++)
        nums[i]=i+1;
        vector<vector<int>>ans(n,vector<int>(n,0));
        int t=0,len=0,top=0,bottom=n-1,left=0,right=n-1,x,y;
        x=y=0;
        while(len!=n*n)
        {
            ans[y][x]=nums[len++];
            if(x==right&&y==top&&t%4==0)t++,top++;
            else if(x==right&&y==bottom&&t%4==1)t++,right--;
            else if(x==left&&y==bottom&&t%4==2)t++,bottom--;
            else if(x==left&&y==top&&t%4==3)t++,
            left++;
            switch(t%4)
            {
                case 0: x++;break;
                case 1:y++;break;
                case 2:x--;break;
                case 3:y--;break;
            } 
        }
        return ans;
    }
};

递归

class Solution {
public:
vector<vector<int>>ans;
bool vis[21][21]={false};
int dir[4][2]={0,1,1,0,0,-1,-1,0};
int n;
vector<vector<int>>mp;
bool check(int x,int y,int& n)
{
    return x>=0&&y>=0&&x<n&&y<n;
}
void dfs(int x,int y,int d,int n,int pos)
{
    vis[x][y]=true;
    mp[x][y]=pos;
    if(check(x+dir[d][0],y+dir[d][1],n)&&!vis[x+dir[d][0]][y+dir[d][1]])
    dfs(x+dir[d][0],y+dir[d][1],d,n,pos+1);
    else
    {
    d=(d+1)%4;
     if(check(x+dir[d][0],y+dir[d][1],n)&&!vis[x+dir[d][0]][y+dir[d][1]])
     dfs(x+dir[d][0],y+dir[d][1],d,n,pos+1);
     else
     return;
    }
}
    vector<vector<int>> generateMatrix(int n) {
      mp=vector<vector<int>>(n,vector<int>(n));
        dfs(0,0,0,n,1);
        return mp;
    }
};

62. 不同路径

难度中等

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。

问总共有多少条不同的路径?

示例 1:

img

输入:m = 3, n = 7
输出:28

示例 2:

输入:m = 3, n = 2
输出:3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右
3. 向下 -> 向右 -> 向下

示例 3:

输入:m = 7, n = 3
输出:28

示例 4:

输入:m = 3, n = 3
输出:6

提示:

  • 1 <= m, n <= 100
  • 题目数据保证答案小于等于 2 * 109

经典dp写法

我们令 dp[i] [j] 是到达 i, j 最多路径

动态方程:dp[i] [j] = dp[i-1] [j] + dp[i] [j-1]

注意,对于第一行 dp[0] [j],或者第一列 dp[i] [0],由于都是在边界,所以只能为 1

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<vector<int>> f(m, vector<int>(n));
        for (int i = 0; i < m; ++i) {
            f[i][0] = 1;
        }
        for (int j = 0; j < n; ++j) {
            f[0][j] = 1;
        }
        for (int i = 1; i < m; ++i) {
            for (int j = 1; j < n; ++j) {
                f[i][j] = f[i - 1][j] + f[i][j - 1];
            }
        }
        return f[m - 1][n - 1];
    }
};

数学排列组合

机器人一定会走m+n-2步,即从m+n-2中挑出m-1步向下走,即C((m+n-2),(m-1))

注意排列组合的求法

class Solution {
public:
    int uniquePaths(int m, int n) {
        long long ans = 1;
        for (int x = n, y = 1; y < m; ++x, ++y) {
            ans = ans * x / y;
        }
        return ans;
    }
};
/*。

ans = ans * x / y; 疑惑这样一定能保证每次都是整除吗? 然后手算了一下还真是,例如:

第一次(53 / 1)任何数都可整除1

第二次(53*52 / 1*2)连续的2个数中一定有一个是2的倍数

第三次(53*52*51 / 1*2*3)连续的3个数中一定有一个是3的倍数

以此类推,每次都可以整除
*/

63. 不同路径 II

难度中等924收藏分享切换为英文接收动态反馈

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。

现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径?

网格中的障碍物和空位置分别用 10 来表示。

示例 1:

img

输入:obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]]
输出:2
解释:3x3 网格的正中间有一个障碍物。
从左上角到右下角一共有 2 条不同的路径:
1. 向右 -> 向右 -> 向下 -> 向下
2. 向下 -> 向下 -> 向右 -> 向右

示例 2:

img

输入:obstacleGrid = [[0,1],[0,0]]
输出:1

提示:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j]01

和上面本质上是一样的,只要我们强行把障碍物处的路径数置为0就行了,此处使用滚动数组写,节省了一点点空间

滚动数组+dp

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        if(obstacleGrid[0][0])
        return 0;
        int dp[110],m=obstacleGrid.size(),n=obstacleGrid[0].size();
        dp[0]=1;
        for(int i=1;i<n;i++)
        dp[i]=dp[i-1]&&!obstacleGrid[0][i];
        for(int i=1;i<m;i++)
        for(int j=0;j<n;j++)
        {
            if(obstacleGrid[i][j])
            dp[j]=0;
            else if(j>0)
            dp[j]+=dp[j-1];
        }
        return dp[n-1];
    }
};

64. 最小路径和

难度中等1401收藏分享切换为英文接收动态反馈

给定一个包含非负整数的 *m* x *n* 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

**说明:**每次只能向下或者向右移动一步。

示例 1:

img

输入:grid = [[1,3,1],[1,5,1],[4,2,1]]
输出:7
解释:因为路径 1→3→1→1→1 的总和最小。

示例 2:

输入:grid = [[1,2,3],[4,5,6]]
输出:12

提示:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 100

滚动数组+dp

和前面差不多

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
       int dp[201]={grid[0][0]};
       for(int i=1;i<grid[0].size();i++)
       dp[i]=dp[i-1]+grid[0][i];
       for(int i=1;i<grid.size();i++)
       for(int j=0;j<grid[0].size();j++)
       {
           if(j==0)
           dp[j]+=grid[i][j];
           else 
           dp[j]=min(grid[i][j]+dp[j],grid[i][j]+dp[j-1]);
       }
       return dp[grid[0].size()-1];
    }
};

66. 加一

难度简单

给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。

示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。

示例 3:

输入:digits = [0]
输出:[1]

提示:

  • 1 <= digits.length <= 100
  • 0 <= digits[i] <= 9

模拟进位

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
int n = digits.size();
        for (int i = n - 1; i >= 0; --i) {
            if (digits[i] != 9) {
                ++digits[i];
                for (int j = i + 1; j < n; ++j) {
                    digits[j] = 0;
                }
                return digits;
            }
        }

        // digits 中所有的元素均为 9
        vector<int> ans(n + 1);
        ans[0] = 1;
        return ans;
    }
};

74. 搜索二维矩阵

难度中等

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

  • 每行中的整数从左到右按升序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

示例 1:

在这里插入图片描述

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true

示例 2:

在这里插入图片描述

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 100
  • -104 <= matrix[i][j], target <= 104

两种解法,第一种先对每列进行一次二分查找再对行进行二分查找,第二种直接将二维数组当成一个一维数组,然后对整个一维数组进行二分

两次二分

class Solution {
public:
int binarySearchFirstColumn(vector<vector<int>>& matrix, int target) {
    int low = -1, high = matrix.size() - 1;
    while (low < high) {
        int mid = (high - low + 1) / 2 + low;
        if (matrix[mid][0] <= target) {
            low = mid;
        } else {
            high = mid - 1;
        }
    }
    return low;
}
bool binarySearchRow(vector<int> row, int target) {
    int low = 0, high = row.size() - 1;
    while (low <= high) {
        int mid = (high - low) / 2 + low;
        if (row[mid] == target) {
            return true;
        } else if (row[mid] > target) {
            high = mid - 1;
        } else {
            low = mid + 1;
        }
    }
    return false;
}

bool searchMatrix(vector<vector<int>>& matrix, int target) {
    int rowIndex = binarySearchFirstColumn(matrix,target);
    if (rowIndex < 0) {
        return false;
    }
    return binarySearchRow(matrix[rowIndex], target);
}
};

一次二分

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size(), n = matrix[0].size();
        int low = 0, high = m * n - 1;
        while (low <= high) {
            int mid = (high - low) / 2 + low;
            int x = matrix[mid / n][mid % n];
            if (x < target) {
                low = mid + 1;
            } else if (x > target) {
                high = mid - 1;
            } else {
                return true;
            }
        }
        return false;
    }
};

78. 子集

难度中等

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同

最标准的回溯题

class Solution {
public:
    vector<int> t;
    vector<vector<int>> ans;
    void dfs(int cur, vector<int>& nums) {
        if (cur == nums.size()) {
            ans.push_back(t);
            return;
        }
        t.push_back(nums[cur]);
        dfs(cur + 1, nums);
        t.pop_back();
        dfs(cur + 1, nums);
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        dfs(0, nums);
        return ans;
    }
};

79. 单词搜索

难度中等1493收藏分享切换为英文接收动态反馈

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例 1:

在这里插入图片描述

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true

示例 2:

在这里插入图片描述

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "SEE"
输出:true

示例 3:

img

输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCB"
输出:false

提示:

  • m == board.length
  • n = board[i].length
  • 1 <= m, n <= 6
  • 1 <= word.length <= 15
  • boardword 仅由大小写英文字母组成

上下左右搜索回溯,然后注意选过的不能再选,标记一下

回溯

class Solution {
public:
    // 四个方向的下标偏移量
    int dx[4] = {0, -1, 1, 0};
    int dy[4] = {-1, 0, 0, 1};
    int l, m, n;
    bool res = false;
    void dfs(vector<vector<char>>& board, vector<vector<int>>& visited, string word, int i, int j, int k) {
        // 剪枝,之前的过程中已经找到结果,不用再搜索,直接返回
        if (res) {
            return;
        }
        // 越界或访问过,直接返回
        if (i < 0 || i >= m || j < 0 || j >= n || k >= l || visited[i][j]) {
            return;
        }
        // 字母不匹配,直接返回
        if (board[i][j] != word[k]) {
            return;
        }
        // word判断完毕,存在结果
        if (k == l - 1) {
            res = true;
            return;
        }
        // 递归搜索下一个位置
        visited[i][j] = 1;
        for (int p = 0; p < 4; p++) {
            dfs(board, visited, word, i + dx[p], j + dy[p], k + 1);
        }
        visited[i][j] = 0;
    }
    bool exist(vector<vector<char>>& board, string word) {
        m = board.size();
        n = board[0].size();
        l = word.size();
        vector<vector<int>> visited(m, vector<int>(n));
        // 从网格中的每一个点开始搜索
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 剪枝,之前的过程中没有找到结果,且开始字母一致才需要搜索
                if (!res && board[i][j] == word[0]) {
                    dfs(board, visited, word, i, j, 0);
                }
            }
        }
        return res;
    }
};

80. 删除有序数组中的重复项 II

难度中等

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

示例 1:

输入:nums = [1,1,1,2,2,3]
输出:5, nums = [1,1,2,2,3]
解释:函数应返回新长度 length = 5, 并且原数组的前五个元素被修改为 1, 1, 2, 2, 3 。 不需要考虑数组中超出新长度后面的元素。

示例 2:

输入:nums = [0,0,1,1,1,1,2,3,3]
输出:7, nums = [0,0,1,1,2,3,3]
解释:函数应返回新长度 length = 7, 并且原数组的前五个元素被修改为 0, 0, 1, 1, 2, 3, 3 。 不需要考虑数组中超出新长度后面的元素。

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按升序排列

双指针

数组是有序的,直接上双指针

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int n = nums.size();
        if (n <= 2) {
            return n;
        }
        int l = 2, r = 2;
        while (r < n) {
            if (nums[l - 2] != nums[r]) {
                nums[l] = nums[r];
                ++l;
            }
            ++r;
        }
        return l;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值