LeetCode 热题 HOT 100 打卡计划 | 第二十四天 | 每日进步一点点

131 阅读3分钟

图片.png

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第25天,点击查看活动详情

240. 搜索二维矩阵 II

思路

(单调性扫描) O(n+m)

m x n矩阵 matrix中我们可以发现一个性质:对于每个子矩阵右上角的数xx左边的数都小于等于xx下边的数都大于x

图片.png

因此我们可以从整个矩阵的右上角开始枚举,假设当前枚举的数是 x

  • 如果 x 等于target,则说明我们找到了目标值,返回true
  • 如果 x小于target,则 x左边的数一定都小于target,我们可以直接排除当前一整行的数;
  • 如果x 大于target,则 x 下边的数一定都大于target,我们可以直接排序当前一整列的数;

排除一整行就是让枚举的点的横坐标加一,排除一整列就是让纵坐标减一。当我们排除完整个矩阵后仍没有找到目标值时,就说明目标值不存在,返回false

具体过程如下:

  • 1、初始化i = 0, j = matrix[0].size() - 1
  • 2、如果matrix[i][j] == target,返回true
  • 3、如果matrix[i][j] < targeti++,排除一行。
  • 4、如果matrix[i][j] > targetj--,排除一列。
  • 5、如果出界还未找到target,则返回false

时间复杂度分析: 每一步会排除一行或者一列,矩阵一共有 n 行,m 列,所以最多会进行 n+m 步。所以时间复杂度是 O(n+m)。

c++代码

 class Solution {
 public:
     bool searchMatrix(vector<vector<int>>& matrix, int target) {
         int n = matrix.size(), m = matrix[0].size();
         if(!n || !m) return false;
         int i = 0, j = m - 1;
         while(i < n &&j >= 0 ){
             if(target == matrix[i][j]) return true;
             else if(target > matrix[i][j]) i++;
             else if(target < matrix[i][j]) j--;
         }
         return false;
     }
 };

279. 完全平方数

思路

(动态规划 + 背包问题) O(n\sqrt n)

状态表示: f[i]表示通过平方数组成i所需要的最少完全平方数的个数。

状态计算:

每个物品的体积:124,,,j,\sqrt i (j * j <= i)

背包大小为i,我们去枚举每个物品w,物品的体积为w * w,考虑最后一个物品j,有两种选择:

  • 1、不选物品j,则 f[i] = f[i]
  • 2、选物品j,则f[i] = f[i - j * j] + 1

两种选择取最小值,状态转移方程为: f[i] = min(f[i],f[i - j * j] + 1)

初始化: f[0] = 0

c++代码

 class Solution {
 public:
     int numSquares(int n) {
         vector<int>f(n + 1, n);
         f[0] = 0; //初始化
         for(int i = 1; i <= n; i++)
             for(int j = 1; j * j <= n; j++)
                 if(i >= j * j)
                     f[i] = min(f[i], f[i - j * j] + 1);
         return f[n];            
     }
 };

283. 移动零

思路

(双指针) O(n)

给定一个数组 nums,要求我们将所有的 0 移动到数组的末尾,同时保持非零元素的相对顺序。

样例:

图片.png

如样例所示,数组nums = [0,1,0,3,12],移动完成后变成nums = [1,3,12,0,0] ,下面来讲解双指针的做法。

我们定义两个指针,i指针和k指针,i指针用来遍历整个nums数组,k指针用来放置nums数组元素。然后将非0元素按照原有的相对顺序都放置到nums数组前面,剩下的位置都置为0。这样我们就完成了0元素的移动,同时也保持了非0元素的相对顺序。

具体过程如下:

  • 1、定义两个指针ik,初始化i = 0k = 0
  • 2、i指针向后移动,遍整个nums数组,如果 nums[i] != 0,也就是说遇到了非0元素,此时我们就将nums[i]元素放置到nums[k]位置,同时k++后一位。
  • 3、最后将k位置之后的元素都赋值为0

实现细节:

遍历数组可以使用for(int x : nums),这样就少定义一个指针,代码也显得更加简洁。

时间复杂度分析: O(n) ,n是数组的长度,每个位置只被遍历一次。

c++代码

 class Solution {
 public:
     void moveZeroes(vector<int>& nums) {
         int k = 0;
         for(int x : nums){
             if(x != 0) nums[k++] = x;
         }
         while(k < nums.size()) nums[k++] = 0;
     }
 };