王清欢Randy 王清欢Randy
首页
  • 编程语言

    • C/C++ 学习笔记
    • Golang 学习笔记
  • 算法分析

    • LeetCode 刷题笔记
  • 操作系统

    • Linux 基础
    • Vim 实用技巧
    • Shell 脚本编程
    • GDB 学习笔记
  • 开发工具

    • Git 学习笔记
  • 分布式理论

    • 共识算法
    • 分布式事务
  • 数据库内核

    • PostgreSQL
    • Postgres-XL
  • hidb
  • pgproxy
  • 实用技巧
  • 学习方法
  • 资源分享
GitHub (opens new window)
首页
  • 编程语言

    • C/C++ 学习笔记
    • Golang 学习笔记
  • 算法分析

    • LeetCode 刷题笔记
  • 操作系统

    • Linux 基础
    • Vim 实用技巧
    • Shell 脚本编程
    • GDB 学习笔记
  • 开发工具

    • Git 学习笔记
  • 分布式理论

    • 共识算法
    • 分布式事务
  • 数据库内核

    • PostgreSQL
    • Postgres-XL
  • hidb
  • pgproxy
  • 实用技巧
  • 学习方法
  • 资源分享
GitHub (opens new window)
  • 基础算法

    • 双指针

      • 双指针基础
      • 碰撞指针
      • 快慢指针
      • 滑动窗口
    • 二分查找

      • 基础应用
      • 边界收缩
      • 局部有序
    • 排序算法

      • 01八大排序
      • 02快速排序
      • 03归并排序
      • 04桶排序
      • 05堆排序
    • 优先搜索

      • 01递归
      • 02网格结构深度优先搜索
      • 03树结构深度优先搜索
      • 04图结构深度优先搜索
      • 05网格结构广度优先搜索
      • 06树结构广度优先搜索
      • 07图结构广度优先搜索
    • 回溯算法

      • 01递归
      • 02 子集问题
      • 03 全排列问题
      • 04 组合问题
      • 05 回溯搜索问题
  • 基础数据结构

    • 线性表与哈希表

      • 01数组
        • 01 数组
          • STL 容器
          • 一维数组
          • 448 找到所有数组中消失的数字
          • 769 最多能完成排序的块
          • 二维数组
          • 48 旋转图像
          • 566 重塑矩阵
          • 240 搜索二维矩阵 II
      • 02栈和队列
      • 03单调栈
      • 04优先队列
      • 05双端队列
      • 06哈希表
      • 07多重集合
      • 08前缀和
      • 09数据结构设计
    • 字符串

      • 01字符串比较
      • 02回文字符串
      • 03字符串匹配
      • 04字符串算术表达式
    • 单链表

      • 01链表基础操作
      • 02链表遍历
    • 二叉树

      • 01二叉树的属性
      • 02二叉树的操作
      • 03层次遍历
      • 04前中后序遍历
      • 05二叉搜索树的属性
      • 06二叉搜索树的操作
      • 07字典树
      • 08二叉搜索树BST
    • 图

      • 01二分图
      • 02拓扑排序
      • 03并查集
      • 04最小生成树
      • 05最短路径
  • 进阶算法

    • 贪心算法

      • 01跳跃游戏
      • 02分配问题
      • 03区间问题
    • 分治策略

    • 动态规划

      • 01一维动态规划
      • 02二维动态规划
      • 03分割型动态规划
      • 04子序列问题
      • 05背包问题
      • 06字符串编辑问题
      • 07股票交易问题
  • 其他内容

    • 数学问题

      • 01公倍数与公因数
      • 02质数问题
      • 03进制转换问题
      • 04数字字符串求和问题
      • 05众数问题
      • 06中位数问题
      • 07数字处理问题
      • 08随机数问题
    • 位运算

      • 01位运算基础
      • 02妙用异或运算
      • 03二进制特性
  • LeetCode刷题笔记
  • 基础数据结构
  • 线性表与哈希表
王清欢
2023-03-24
目录

01数组

# 01 数组

# STL 容器

​ 容器是可容纳各种数据类型(基本数据类型、对象等)的通用数据结构,都是类模板,分为三种类型:

  • 顺序容器:vector, deque, list
  • 关联容器:set, multiset, map, multimap
  • 容器适配器:stack, queue, priority_queue

顺序容器:

​ 顺序容器是非排序的,而且其元素的插入位置与元素自身的值无关,数组便于查找,链表便于操作。

  • vector #include <vector> 一维动态数组:其元素在内存中是连续存放的,随机存取任何元素都可以在常数时间内完成,在该容器的尾部增删元素也几乎能够在常熟时间内完成具有较好的性能。
  • deque #include <deque> 双向队列:其元素在内存中是连续存放的,随机存取任何元素都可以在常数时间内完成,在该容器的两端增删元素也几乎能够在常熟时间内完成具有较好的性能。
  • list #include <list> 双向链表:其元素在内存中是不连续存放的,不支持随机存取,在该容器的任何位置增删元素几乎都能够在常熟时间内完成具有较好的性能。

关联容器:

​ 关联容器的元素是排序的,插入元素需要根据相应的排序规则来确定插入位置的,在查找时具有较好的性能。关联容器通常使用平衡二叉树实现,其插入和检索的时间都是 $O(log(N))$ 。

  • set/multiset #include <set> 集合:set 集合中不允许存在相同的元素,multiset 集合中允许存在相同的元素。
  • map/multimap #include <map> 键值对集合:map 和 set 的不同在于前者存放的元素有且仅有两个成员变量 (first,second),一个名为 first,另一个名为 second ,first 的值用来对整体元素进行从小到大的排序,并可以通过 first 快速检索元素。和 multiset 类似 multimap 和 map 的区别中允许存在相同 first 值的元素。

容器适配器:

  • stack #include <stack> 栈:栈是项都有限序列,并满足序列中被删除、检索和修改的项都只能是最近插入序列的项,即栈顶的项。满足后进先出规则
  • queue #include <queue> 队列:(入队)插入只允许在尾部进行,(出队)删除、检索和修改只允许在头部进行。满足先进先出规则
  • priority_queue #include <queue> 优先级队列:优先级最高的元素总是第一个出队列

更多关于C++ STL的内容请移步到此博文 (opens new window)

# 一维数组

# 448 找到所有数组中消失的数字 (opens new window)

给定一个长度为 n 的数组,其中包含范围为 1 到 n 的整数,有些整数重复了多次,有些整数没有出现,求 1 到 n 中没有出现过的整数。

输入是一个一维整数数组,输出也是一个一维整数数组,表示输入数组内没出现过的数字。

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

解析:

​ 利用数组这种数据结构建立长度为 n 的标记数组,每一个标记表示对应位置的元素是否出现过。然后遍历一遍 nums 根据其元素情况修改标记数组对应元素。完成一遍遍历之后,标记数组中就已经记录了 nums 中所有元素出现情况,最后在遍历一遍标记数组就可以得到没出现的元素了。

数组 [1,2,3,4,5,6,7,8]
nums [4,3,2,7,8,2,3,1]
mask [T,T,T,T,F,F,T,T]
class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        int len = nums.size();
        // 记录nums中元素存在情况
        vector<bool> mask(len,true);
        for(const auto num: nums){
            mask[num-1] = false;
        }
        // 找出不存在的元素
        vector<int> ans;
        for(int i=0;i<len;++i){
            if(mask[i]){
                ans.push_back(i+1);
            }
        }
        return ans;
    }
};

​ 进一步地,可以直接对原数组进行标记:把重复出现的数字在原数组出现的位置设为负数,最后仍然为正数的位置即为没有出现过的数。

# 769 最多能完成排序的块 (opens new window)

给定一个含有 0 到 n 整数的数组,每个整数只出现一次,求这个数组最多可以分割成多少个子数组,使得对每个子数组进行增序排序后,原数组也是增序的。

输入一个一维整数数组,输出一个整数,表示最多的分割数。

输入: arr = [1,0,2,3,4] 输出: 4 解释: 可以把它分成两块,例如 [1, 0], [2, 3, 4]。然而,分成 [1, 0], [2], [3], [4] 可以得到最多的块数。

解析:

从左往右遍历,同时记录当前的最大值,每当当前最大值等于数组位置时,我们可以多一次分割。

为什么可以通过这个算法解决问题呢?

如果当前最大值大于数组位置,则说明右边一定有小于数组位置的数字,需要把它也加入待排序的子数组;又因为数组只包含不重复的 0 到 n,所以当前最大值一定不会小于数组位置。所以每当当前最大值等于数组位置时,假设为 p,我们可以成功完成一次分割,并且其与上一次分割位置 q 之间的值一定是 q + 1 到 p 的所有数字。

class Solution {
public:
    int maxChunksToSorted(vector<int>& arr) {
        int cur_max = INT_MIN;
        int ans = 0;
        for(int i=0;i<arr.size();++i){
            cur_max = max(cur_max,arr[i]);
            if(cur_max == i){
                ++ans;
            }
        }
        return ans;
    }
};

# 二维数组

# 48 旋转图像 (opens new window)

给定一个 n × n 的矩阵,在尽量不创建额外储存空间的情况下,求它顺时针旋转 90 度的结果,且必须在原矩阵上修改(in-place)。

输入和输出都是一个二维整数矩阵。

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

解析:

​ 本题可以每次只考虑矩阵一圈中的 4 个间隔90度的元素的旋转情况,设置两层循环:外层循环用于一圈一圈的遍历矩阵,内循环用于旋转一圈上的元素。

​ 旋转公式主要与遍历的当前圈层相关,例如,矩阵长度len = n + 1,则第 i 圈中与[i][j]参与旋转的其他3个元素分别为[j][n-j], [n-i][n-j], [n-j][i] 他们的旋转公式为:

// 以下图示例中的 2,8,15,9 为例
temp = matrix[j][n-i]; // 8
matrix[j][n-i] = matrix[i][j]; // 2
matrix[i][j] = matrix[n-j][i]; // 9
matrix[n-j][i] = matrix[n-i][n-j]; // 15
matrix[n-i][n-j] = temp;
// 经过上述旋转得到 9,2,8,15
48
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size() - 1;
        for(int i=0;i<=n/2;++i){
            // 子矩阵的第一个元素是[i][n-i],最后一个元素是[n-i][n-i]
            for(int j=i;j<n-i;++j){
                int tmp = matrix[j][n-i];
                matrix[j][n-i] = matrix[i][j];
                matrix[i][j] = matrix[n-j][i];
                matrix[n-j][i] = matrix[n-i][n-j];
                matrix[n-i][n-j] = tmp;
            }
        }
    }
};

# 566 重塑矩阵 (opens new window)

给定一个由二维数组 mat 表示的 m x n 矩阵,以及两个正整数 r 和 c ,分别表示想要的重构的矩阵的行数和列数。重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。如果参数不合理直接返回原数组。

输入一个二维数组,输出一个根据题目要去重塑后的二维数组

输入:mat = [[1,2],[3,4]], r = 2, c = 4
输出:[[1,2],[3,4]]

解析:

​ 本题可以利用二维矩阵可以映射为一维矩阵的特点解决,例如一个 m x n 的二维矩阵可以映射成一个长度为m*n 的一维数组,其元素映射关系为:array[i] = matrix[i/n][i%n]。根据这种映射关系,又可以将一维矩阵映射为二维矩阵。

​ 所以本题的解法就是将一个 m x n 的二维矩阵映射成一个一维数组,再将这个一维数组映射成一个 r x c 的二维矩阵。

class Solution {
public:
    vector<vector<int>> matrixReshape(vector<vector<int>>& mat, int r, int c) {
        int m = mat.size(), n = mat[0].size();
        if(m==0 || n==0){
            return {{}};
        }
        // 不合理的参数直接返回原矩阵
        if( r*c != m*n){
            return mat;
        }
        // 一维数组到二维的映射
        vector<vector<int>> ans(r,vector<int>(c));
        for(int i=0;i<m*n;++i){
            ans[i/c][i%c] = mat[i/n][i%n];
        }
        return ans;
    }
};

# 240 搜索二维矩阵 II (opens new window)

给定一个二维矩阵,已知每行和每列都是增序的,尝试设计一个快速搜索一个数字是否在矩阵中存在的算法。

输入是一个二维整数矩阵,和一个待搜索整数。输出是一个布尔值,表示这个整数是否存在于矩阵中。

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5 输出:true

解析:

本题可以利用矩阵中元素的增序快速缩减搜索空间,每一行是增序的,每一列是增序的。

那么我们从矩阵的右上角开始查找:

  • 如果当前值大于目标值那么就直接排除了当前列,向左移动一位
  • 如果当前值小于搜索值,由于我们已经搜索了当前行当前值右侧的所有元素,其左侧的值均小于当前值,因此可以直接排除当前行,向下移动一位
  • 如果最终移动到左下角时仍没有找到目标值,则说明待搜索值不存在于矩阵中
class Solution {
public:
    bool searchMatrix_2(vector<vector<int>>& matrix, int target) {
        if(matrix.empty()||matrix[0].empty()){
            return false;
        }
        int m = matrix.size(), n = matrix[0].size();
        int i = 0, j = n-1;
        while(i<m && j>=0){
            if(matrix[i][j] == target){
                return true;
            }else if(matrix[i][j] > target){
                // 大于目标值向左移动一步
                --j;
            }else{
                // 小于目标值向下移动一步
                ++i;
            }
        }
        return false;
    }
};
上次更新: 2023/11/19, 12:55:48
05 回溯搜索问题
02栈和队列

← 05 回溯搜索问题 02栈和队列→

Theme by Vdoing | Copyright © 2023-2024 Wang Qinghuan | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式