day02 | 209长度最小子数组[双指针\滑窗]|977有序数组的平方[双指针]|59螺旋矩阵II

0.引言

1.有序数组的平方

Category Difficulty Likes Dislikes
algorithms Easy (68.47%) 733 -

给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

提示:

  • 1 <= nums.length <= 10<sup>4</sup>
  • -10<sup>4</sup> <= nums[i] <= 10<sup>4</sup>
  • nums 已按 非递减顺序 排序

进阶:

  • 请你设计时间复杂度为 O(n) 的算法解决本问题

Discussion | Solution

1.1.自己想法及代码实现

第一想法是平方后排序,或者把负数转为正数后排序再平方。

/*
 * @lc app=leetcode.cn id=977 lang=cpp
 *
 * [977] 有序数组的平方
 */

// @lc code=start
class Solution {
 public:
  vector<int> sortedSquares(vector<int>& nums) {
    for (auto& i : nums) {
      i *= i;
    }
    std::sort(nums.begin(), nums.end());
    return nums;
  }
};
// @lc code=end

显然调用排序函数时间复杂度不是O(1)。还是要从排序入手。双指针法:

class Solution {
 public:
  vector<int> sortedSquares(vector<int>& nums) {
    if (nums.empty()) return nums;

    for (auto& i : nums) {
      if (i < 0) i *= -1;
    }

    int nums_size = nums.size();
    std::vector<int> res(nums_size);

    for (int left = 0, right = nums.size() - 1; left <= right;) {
      if (nums[left] > nums[right]) {
        res[--nums_size] = nums[left];
        left++;
        // res[--nums_size] = nums[left++]; //可以简化为
      } else {
        res[--nums_size] = nums[right];
        right--;
      }
    }

    for (auto& i : res) {
      i *= i;
    }

    return res;
  }
};
  • 思想:从两端开始找大的数,存储到新的数组中。
  • 之前的想法一直在原地排序上,卡住了,时间和空间还真不能兼顾啊!

1.2.参考想法及代码实现

  • 大差不差
image.png

2.长度最小的子数组

Category Difficulty Likes Dislikes
algorithms Medium (47.32%) 1589 -

给定一个含有 n个正整数的数组和一个正整数 target** 。**

找出该数组中满足其和≥ target的长度最小的 连续子数组 [nums<sub>l</sub>, nums<sub>l+1</sub>, ..., nums<sub>r-1</sub>, nums<sub>r</sub>] ,并返回其长度如果不存在符合条件的子数组,返回 0

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 10<sup>9</sup>
  • 1 <= nums.length <= 10<sup>5</sup>
  • 1 <= nums[i] <= 10<sup>5</sup>

进阶:

  • 如果你已经实现O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

Discussion | Solution

2.1.自己想法及代码实现

第一想法是先排序,然后暴力查找,不对,他要求是连续子数组。

第二想法快慢指针,俩指针之间的数就是满足条件的,同时要遍历完也就需要维护一个 min_len:

class Solution {
 public:
  int minSubArrayLen(int target, vector<int>& nums) {
    if (nums.empty()) return 0;
    int slow_idx = 0, fast_idx = 0;
    int min_len = std::numeric_limits<int>::max();
    while (fast_idx < nums.size()) {
      int sum = nums_sum(nums, slow_idx, fast_idx);
      if (sum >= target) {
        min_len = std::min(min_len, fast_idx - slow_idx + 1);
        slow_idx ++;
      } else{
        fast_idx++;
      } 
    }
    return min_len == std::numeric_limits<int>::max() ? 0 : min_len;
  }

 private:
  int nums_sum(vector<int>& nums, int i, int j) {
    int res = 0;
    for (; i <= j; ++i) {
      res += nums[i];
    }
    return res;
  }
};

自己实现的代码有case没通过。

我再看了一下,不是case没通过,是超时了,思想应该是没问题的。

之前把题目条件看为==了:

// 题目是 >= target,这个搞成 == 了 
class Solution {
 public:
  int minSubArrayLen(int target, vector<int>& nums) {
    if (nums.empty()) return 0;
    int slow_idx = 0, fast_idx = 0;
    int min_len = std::numeric_limits<int>::max();
    while (fast_idx < nums.size()) {
      int sum = nums_sum(nums, slow_idx, fast_idx);
      //   std::cout << sum << " ";
      if (target == sum) {
        min_len = std::min(min_len, fast_idx - slow_idx + 1);
        if (min_len == 1) return min_len;
        slow_idx = fast_idx;
      } else if (sum < target) {
        fast_idx++;
      } else {
        slow_idx++;
      }
    }
    return min_len == std::numeric_limits<int>::max() ? 0 : min_len;
  }

 private:
  int nums_sum(vector<int>& nums, int i, int j) {
    int res = 0;
    for (; i <= j; ++i) {
      res += nums[i];
    }
    return res;
  }
};

暴力解法:

  • 两层for循环,一个为起始位置,一个为结束位置,记录最小的len:
// 暴力法
class Solution {
 public:
  int minSubArrayLen(int target, vector<int>& nums) {
    if (nums.empty()) return 0;
    int start_idx = 0, end_idx = 0;
    int min_len = std::numeric_limits<int>::max();
    for (int start_idx = 0; start_idx < nums.size(); ++start_idx) {
      for (int end_idx = start_idx; end_idx < nums.size(); ++end_idx) {
        if (nums_sum(nums, start_idx, end_idx) >= target) {
          min_len = std::min(min_len, end_idx - start_idx + 1);
        }
      }
    }
    return min_len == std::numeric_limits<int>::max() ? 0 : min_len;
  }

 private:
  int nums_sum(vector<int>& nums, int i, int j) {
    int res = 0;
    for (; i <= j; ++i) {
      res += nums[i];
    }
    return res;
  }
};

这里的nums_sum函数可以优化,不用每次每次都求和,加减变动的元素就行。为了更好的理解思想先就这样吧,优化不难。

还是优化一下吧:

class Solution {
 public:
  int minSubArrayLen(int target, vector<int>& nums) {
    if (nums.empty()) return 0;
    int slow_idx = 0, fast_idx = 0;
    int min_len = std::numeric_limits<int>::max();
    int nums_sum = 0;
    while (fast_idx < nums.size()) {
      nums_sum += nums[fast_idx];
      std::cout << nums_sum << " ";
      if (nums_sum >= target) {
        min_len = std::min(min_len, fast_idx - slow_idx + 1);
        nums_sum -= nums[slow_idx];
        nums_sum -= nums[fast_idx];  // 进入这个逻辑后 fast_idx被加了两次
        slow_idx++;
      } else {
        fast_idx++;
      }
    }
    return min_len == std::numeric_limits<int>::max() ? 0 : min_len;
  }
};

2.2.参考想法及代码实现

  • 大差不差。有一个优化的地方,就是找到最小的后break,往后只会更长,不需要遍历了,算是剪枝,这点遗忘了:
// 暴力法
class Solution {
 public:
  int minSubArrayLen(int target, vector<int>& nums) {
    if (nums.empty()) return 0;
    int start_idx = 0, end_idx = 0;
    int min_len = std::numeric_limits<int>::max();
    for (int start_idx = 0; start_idx < nums.size(); ++start_idx) {
      int nums_sum = 0;
      for (int end_idx = start_idx; end_idx < nums.size(); ++end_idx) {
        nums_sum += nums[end_idx];
        if (nums_sum >= target) {
          min_len = std::min(min_len, end_idx - start_idx + 1);
          break; // 往后只会更长
        }
      }
    }
    return min_len == std::numeric_limits<int>::max() ? 0 : min_len;
  }
};

3. 螺旋矩阵 II

Category Difficulty Likes Dislikes
algorithms Medium (73.96%) 958 -

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= n <= 20

Discussion | Solution

3.1.自己想法及代码实现

感觉就是找规律,一圈一圈的填充:

class Solution {
 public:
  vector<vector<int>> generateMatrix(int n) {
    if (n == 0) return {};
    int rows = n, cols = n;

    vector<vector<int>> matrix(rows, vector<int>(cols));

    int i_ceil = 0;
    int i_floor = rows - 1;
    int j_ceil = 0;
    int j_floor = cols - 1;
    int val = 1;
    while (i_ceil <= i_floor && j_ceil <= j_floor) {
      fill_one_circle(matrix, i_ceil, i_floor, j_ceil, j_floor, val);
      i_ceil++, i_floor--;
      j_ceil++, j_floor--;
    }
    return matrix;
  }

 private:
  void fill_one_circle(vector<vector<int>>& matrix, int i_ceil, int i_floor,
                       int j_ceil, int j_floor, int& val) {
    for (int j = j_ceil; j <= j_floor; ++j) {  // 上
      matrix[i_ceil][j] = val++;
    }
    for (int i = i_ceil + 1; i <= i_floor; ++i) {  // 右
      matrix[i][j_floor] = val++;
    }
    for (int j = j_floor - 1; j >= j_ceil; --j) {  // 下
      matrix[i_floor][j] = val++;
    }
    for (int i = j_floor - 1; i > i_ceil; --i) {  // 左
      matrix[i][j_ceil] = val++;
    }
  }
};

3.2.参考思想及代码实现

先占坑

填坑:

算法流程:

  • 空值处理: 当 matrix 为空时,直接返回空列表 [] 即可。
  • 初始化: 矩阵 左、右、上、下 四个边界 l , r , t , b ,用于打印的结果列表 res 。
  • 循环打印: “从左向右、从上向下、从右向左、从下向上” 四个方向循环,每个方向打印中做以下三件事 (各方向的具体信息见下表) ;
    • 1.根据边界填充,即将元素按顺序添加;
    • 2.边界向内收缩 1,(代表已被打印);
    • 3.判断是否填充完毕(边界是否相遇),若填充完毕则跳出。
  • 返回值: 返回 res 即可。
image.png
class Solution {
 public:
  vector<vector<int>> generateMatrix(int n) {
    if (n == 0) return {};
    int left = 0, right = n - 1, top = 0, bottom = n - 1, val = 1;
    vector<vector<int>> matrix(n, vector<int>(n));
    while (true) {
      for (int i = left; i <= right; ++i)  matrix[top][i] = val++;     // left to right.
      if (++top > bottom) break;  // top 内陷
      for (int i = top; i <= bottom; ++i)  matrix[i][right] = val++;  // top to bottom.
      if (left > --right) break;  // right 内陷
      for (int i = right; i >= left; --i)  matrix[bottom][i] = val++;  // right to left.
      if (top > --bottom) break;  // bottom 内陷
      for (int i = bottom; i >= top; --i)  matrix[i][left] = val++;  // bottom to top.
      if (++left > right) break;  // left内陷
    }
    return matrix;
  }
};

这个方法非方阵也能填充。内陷这个思路特别好!!!

4.总结

  • 双指针
  • 动态窗口
  • 注意时间和空间的取舍
  • 想不到好办法的时候暴力法也是不错的选择。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容