第一章 数组

news/2024/11/21 1:26:20/

目录

  • 一、二分查找
    • 1.1 二分查找母题
    • 1.2 搜索插入位置
    • 1.3 在排序数组中查找元素的第一个和最后一个位置
    • 1.4 x 的平方根
    • 1.5 有效的完全平方数
  • 二、双指针
    • 2.1 移除元素
    • 2.2 删除有序数组中的重复项
    • 2.3 移动零
    • 2.4 比较含退格的字符串
    • 2.5 有序数组的平方
  • 三、滑动窗口
    • 3.1 长度最小的子数组
    • 3.2 水果成篮
    • 3.3 最小覆盖子串
  • 四、螺旋矩阵
    • 4.1 螺旋矩阵
    • 4.2 螺旋矩阵Ⅱ
    • 4.3 螺旋矩阵Ⅲ
    • 4.4 螺旋矩阵Ⅳ

一、二分查找

1.1 二分查找母题

Leetcode 704

二分算法使用前提:

  • 数组有序
  • 元素不重复

二分算法中,对于区间的不同定义,决定着二分算法中代码不同写法。

写法一:定义 target 定义在一个左闭右闭的区间里面,即 [left, right]

那么这时候代码需要注意:

  • 循环 while (left <= right) 要使用 <= ,因为 left == right 是有意义的,所以使用 <=
  • 代码 if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个 nums[middle] 一定不是 target,那么接下来要查找的左区间结束下标位置就是 middle - 1

代码如下:

class Solution {
public:int search(vector<int>& nums, int target) {int left = 0, right = nums.size() - 1;while (left <= right) {int mid = left + ((right - left) >> 1);  // 防止溢出if (nums[mid] > target) right = mid - 1;else if (nums[mid] < target) left = mid + 1;else return mid;}return -1;}
};
  • 时间复杂度:O(logn)
  • 空间复杂度:O(1)

写法二:定义 target 定义在一个左闭右开的区间里 [left, right)

那么这之后写代码需要注意:

  • 循环 while (left < right),这里使用 < ,因为 left == right 在区间 [left, right) 是没有意义的
  • 代码 if (nums[middle] > target)right 更新为 middle,因为当前 nums[middle] 不等于 target,去左区间继续寻找,而寻找区间是左闭右开区间,所以 right 更新为 middle,即:下一个查询区间不会去比较 nums[middle]

代码如下:

class Solution {
public:int search(vector<int>& nums, int target) {int left = 0, right = nums.size();while (left < right) {int mid = left + ((right - left) >> 1);  // 防止溢出if (nums[mid] > target) right = mid;else if (nums[mid] < target) left = mid + 1;else return mid;}return -1;}
};
  • 时间复杂度:O(logn)
  • 空间复杂度:O(1)

1.2 搜索插入位置

Leetcode 35

很明显使用二分查找,但是要处理一种特殊情况,即所查找元素不存在的时候,返回应该插入的位置。

对于插入元素,有四种插入位置:

  • 目标值比数组中所有元素小,[0, -1]
  • 目标值等于数组中的一个元素,return mid
  • 目标值插入数组中的位置,[left, right], return right + 1
  • 目标值比所有元素都大,return right + 1

这里要注意二分代码中,优先动的永远是右指针 right

class Solution {
public:int searchInsert(vector<int>& nums, int target) {int l = 0, r = nums.size() - 1;while (l <= r) {int mid = l + ((r - l) >> 1);if (nums[mid] > target) r = mid - 1;else if (nums[mid] < target) l = mid + 1;else return mid;}return r + 1;}
};
  • 时间复杂度:O(logn)
  • 空间复杂度:O(1)

1.3 在排序数组中查找元素的第一个和最后一个位置

Leetcode 34

这个题目有三种情况:

  • 情况一:target 不在数组的大小范围内
  • 情况二:target 在数组的大小范围内,但是 target 不在数组中
  • 情况三:target 在数组的大小范围内且在数组中
class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {int leftBorder = getLeftBorder(nums, target);int rightBorder = getRightBorder(nums, target);if (leftBorder == -2 || rightBorder == -2) return {-1, -1}; // 情况一if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder -1}; // 情况三return {-1, -1}; // 情况二}// 寻找右边界int getRightBorder(vector<int> &nums, int target) {int l = 0, r = nums.size() - 1;int rightBorder = -2; // 初始值while (l <= r) {int mid = l + ((r - l) >> 1);if (nums[mid] > target) r = mid - 1;else l = mid + 1, rightBorder = l;}return rightBorder;}// 寻找左边界int getLeftBorder(vector<int> &nums, int target) {int l = 0, r = nums.size() - 1;int leftBorder = -2;while (l <= r) {int mid = l + ((r - l) >> 1);if (nums[mid] >= target) r = mid - 1, leftBorder = r; // 需要在 nums[mid] = target 的时候更新 leftBordelse l = mid + 1;}return leftBorder;}
};

1.4 x 的平方根

Leetcode 69

class Solution {
public:int mySqrt(int x) {int l = 0, r = x, res = -1;while (l <= r) {int mid = l + ((r - l) >> 1);if ((long long) mid * mid <= x)res = mid, l = mid + 1;else r = mid - 1;}return res;}
};

1.5 有效的完全平方数

Leetcode 367

class Solution {
public:bool isPerfectSquare(int num) {int l = 0, r = num;while (l <= r) {int mid = l + ((r - l) >> 1);if ((long long) mid * mid < num) l = mid + 1;else if ((long long) mid * mid > num) r = mid - 1;else return true;}return false;}
};

二、双指针

2.1 移除元素

Leetcode 27

class Solution {
public:int removeElement(vector<int>& nums, int val) {int slowIndex = 0;for (int fastIndex = 0; fastIndex < nums.size(); fastIndex ++ ) if (nums[fastIndex] != val)nums[slowIndex ++ ] = nums[fastIndex];return slowIndex;}
};

2.2 删除有序数组中的重复项

Leetcode 26

class Solution {
public:int removeDuplicates(vector<int>& nums) {int slow = 1;for (int fast = 1; fast < nums.size(); fast ++ )if (nums[fast] != nums[fast - 1])nums[slow ++ ] = nums[fast];return slow;}
};

2.3 移动零

Leetcode 283

双指针操作,快指针指向待处理序列头部,慢指针指向当前已经处理好的序列尾部

快指针不断向右移动,每次右移指向非零元素时,则将快指针与慢指针指向的元素交换,同时往右移动慢指针

这里有两个性质:

  • 慢指针左边元素均非零
  • 快指针左边直到慢指针处的元素均为零

可以通过两次遍历,先将所有非零元素移到前面,然后将后面位置补 0 即可,但是这样做有违题意——移动零,不是 “补零”。

class Solution {
public:void moveZeroes(vector<int>& nums) {int slow = 0;for (int fast = 0; fast < nums.size(); fast ++ )if (nums[fast]) swap(nums[slow ++ ], nums[fast]);}
};

2.4 比较含退格的字符串

Leetcode 844

class Solution {
public:void rebuild(string &s) {int slow = 0;for (char c : s) if (c == '#') {if (slow > 0) slow -- ;} else s[slow ++ ] = c;s.resize(slow);}bool backspaceCompare(string s, string t) {rebuild(s);rebuild(t);return s == t;}
};

2.5 有序数组的平方

Leetcode 977

class Solution {
public:vector<int> sortedSquares(vector<int>& nums) {vector<int> res(nums.size(), 0);int l = 0, r = nums.size() - 1;int index = r;while (l <= r) if (-nums[l] > nums[r]) res[index -- ] = nums[l] * nums[l], l ++ ;else res[index -- ] = nums[r] * nums[r], r -- ;return res;}
};

三、滑动窗口

3.1 长度最小的子数组

Leetcode 209

class Solution {
public:int minSubArrayLen(int target, vector<int>& nums) {int res = INT32_MAX;int sum = 0, subLength = 0; // 窗口内的和、窗口长度for (int i = 0, j = 0; j < nums.size(); j ++ ) { // i表示窗口的起始位置sum += nums[j];while (sum >= target) {subLength = j - i + 1;res = min(res, subLength);sum -= nums[i ++ ]; // 移动窗口左边界}}return res == INT32_MAX ? 0 : res;}
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

时间复杂度为 O ( n ) O(n) O(n),主要看数组中每一个元素的被操的次数,每个元素被操作两次,一次进入窗口、一处出窗口,时间复杂度为 O ( 2 n ) O(2n) O(2n),即 O ( n ) O(n) O(n)

3.2 水果成篮

Leetcode 904

class Solution {
public:int totalFruit(vector<int>& fruits) {int n = fruits.size();unordered_map<int, int> cnt;int res = 0;for (int l = 0, r = 0; r < n; r ++ ) {cnt[fruits[r]] ++ ;while (cnt.size() > 2) {auto it = cnt.find(fruits[l]);it->second -- ;if (!it->second) cnt.erase(it);l ++ ;}res = max(res, r - l + 1);}return res;}
};

3.3 最小覆盖子串

Leetcode 76

class Solution {
public:string minWindow(string s, string t) {unordered_map<char, int> tmap, smap;int correct = 0;string res = s + "initialize a max string";// 构建t中字符出现的字典for (auto c : t)tmap[c] ++ ;for (int l = 0, r = 0; r < s.size(); r ++ ) {smap[s[r]] ++ ;if (tmap[s[r]] >= smap[s[r]]) // 表示这是一次有效的添加correct ++ ;while (l < r && smap[s[l]] > tmap[s[l]])smap[s[l ++ ]] -- ;if (correct == t.size()) {if (r - l + 1 < res.size())res = s.substr(l, r - l + 1);}}return res == s + "initialize a max string" ? "" : res;}
};

四、螺旋矩阵

4.1 螺旋矩阵

Leetcode 54

class Solution {
public:vector<int> spiralOrder(vector<vector<int>>& matrix) {vector<int> res;int n = matrix.size(), m = matrix[0].size();if (!n) return res;vector<vector<bool>> st(n, vector<bool>(m));int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};for (int i = 0, x = 0, y = 0, d = 0; i < n * m; i ++ ) {res.push_back(matrix[x][y]);st[x][y] = true;int a = x + dx[d], b = y + dy[d];if (a < 0 || a >= n || b < 0 || b >= m || st[a][b]) {d = (d + 1) % 4;a = x + dx[d], b = y + dy[d];}x = a, y = b;}return res;}
};

4.2 螺旋矩阵Ⅱ

Leetcode 59

class Solution {
public:vector<vector<int>> generateMatrix(int n) {vector<vector<int>> res(n, vector<int> (n, 0));int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};for (int i = 0, x = 0, y = 0, d = 0; i < n * n; i ++ ) {res[x][y] = i + 1;int a = x + dx[d], b = y + dy[d];if (a < 0 || a >= n || b < 0 || b >= n || res[a][b]) {d = (d + 1) % 4;a = x + dx[d], b = y + dy[d];}x = a, y = b;}return res;}
};

4.3 螺旋矩阵Ⅲ

Leetcode 885

class Solution {
public:vector<vector<int>> spiralMatrixIII(int rows, int cols, int x, int y) {vector<vector<int>> res;int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};res.push_back({x, y});int d = 0, tot = rows * cols;for (int k = 1; res.size() < tot; k ++ ) // k:路径长度for (int i = 0; i < 2 && res.size() < tot; i ++ ) { // 每个长度会走两次for (int j = 0; j < k && res.size() < tot; j ++ ) { // 每一次长度都是kint a = x + dx[d], b = y + dy[d];if (a >= 0 && a < rows && b >= 0 && b < cols)res.push_back({a, b});x = a, y = b;}d = (d + 1) % 4;}return res;}
};

4.4 螺旋矩阵Ⅳ

Leetcode 2326

class Solution {
public:vector<vector<int>> spiralMatrix(int m, int n, ListNode* head) {vector<vector<int>> res(m, vector<int>(n, -1));int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};for (int i = 0, x = 0, y = 0, d = 0; i < n * m && head != nullptr; i ++ ) {res[x][y] = head->val;int a = x + dx[d], b = y + dy[d];if (a < 0 || a >= m || b < 0 || b >= n || res[a][b] != -1) {d = (d + 1) % 4;a = x + dx[d], b = y + dy[d];}x = a, y = b;head = head->next;}return res;}
};

http://www.ppmy.cn/news/66733.html

相关文章

HADOOP入门

1.Hadoop简介 组件 Hadoop由4部分组成 1)HDFS:(Hadoop Distribute File System)分布式文件系统,海量数据存储解决方案 2)MapReduce:Hadoop的分布式运算编程框架 3)Yarn:分布式资源调度平台和任务监控平台 4)Commons: HADOOP底层技术支持 主要用来解决:大数据存储,大数据分…

docker入门和docker应用场景,镜像制作,服务编排,docker私服

一、简介 docker解决了什么问题docker和虚拟机的区别在CentOS7里安装docker 1. docker简介 我们写的代码会接触到好几个环境&#xff1a;开发环境、测试环境以及生产环境等等。多种环境去部署同一份代码&#xff0c;由于环境原因往往会出现软件跨环境迁移的问题&#xff08;也就…

DB2_sql_问题

db2新增字段指定顺序 这个是不能做到的&#xff0c;除非把表删除重新创建的&#xff01; 原理是这样子的&#xff1a;当你创建表时系统会记录下你的SEQ-ID,就是字段的顺序号&#xff0c;这个是根据字段先后顺序来生成的&#xff0c;系统默认显示的时候也是根据这个来的&#x…

设计模式 - 工厂

文章参考来源 一、概念 创建简单的对象直接 new 一个就完事&#xff0c;但对于创建时需要各种配置的复杂对象例如手机&#xff0c;没有工厂的情况下&#xff0c;用户需要自己处理屏幕、摄像头、处理器等配置&#xff0c;这样用户和手机就耦合在一起了。 可以使代码结构清晰&a…

Unity物理系统脚本编程(下)

一、修改物理材质 Unity对物体表面材料的性质做了件化处理&#xff0c;仅有5种常用属性&#xff1a; Dynamic Friction&#xff08;动态摩擦系数&#xff09;Static Friction&#xff08;静态摩擦系数&#xff09;Bounciness&#xff08;弹性系数&#xff09;Friction Combine…

ES6函数中参数的默认值和解构赋值

ES6函数中参数的默认值 ●给函数的形参设置一个默认值, 当你没有传递实参的时候, 使用默认值 ●直接使用 赋值符号() 给形参赋值即可 function fn(a, b 100) {console.log(a, b)}fn()fn(10)fn(10, 20)复制代码 ES6的函数默认值 ●在ES5之前是没有函数默认值的。函数的默认值是…

金字塔特征融合

金字塔的三种主要结构 FPN: Feature Pyramid Networks for Object Detection (CVPR 2017) PANet: Path Aggregation Network for Instance Segmentation (CVPR 2018) BiFPN: EfficientDet: Scalable and Efficient Object Detection (CVPR 2020) Deep High-Resolution Repre…

软件测试报告模板

目录 2 1 概述... 3 1.1 测试目的... 3 1.2 测试策略... 3 1.3 测试方法... 3 1.4 计划验收标准... 3 1.5 测试用例... 4