0%

【数据结构】线性数据结构

线性数据结构

摘要

对LeetCode上各种线性数据结构相关的题目做了一个分类整理,主要内容来源于LeetCode官方学习内容,这里只是一个记录和梳理,后序将持续更新。

1、数组和字符串

二分查找

一般用于给定的数组是有序的,或先手动排序

搜索插入位置

寻找旋转排序数组中的最小值

首尾双指针

两数之和 II - 输入有序数组

快慢双指针

移除元素
最大连续 1 的个数
长度最小的子数组
删除有序数组中的重复项
移动零

滑动窗口

子数组最大平均数
可获得的最大点数
爱生气的书店老板
定长子串中元音的最大数目
将 x 减到 0 的最小操作数
最小覆盖子串

字符串匹配KMP算法

关键在于构建next数组的方法

二维数组

一般就是矩阵问题,矩阵问题后面在动态规划和其他算法问题中也会经常遇到。

旋转矩阵

零矩阵

对角线遍历

2、链表

递归

链表的很多问题都可以用递归,典型问题比如:两两交换链表中的节点

快慢指针

环形链表II
相交链表

经典问题

反转链表
回文链表

一般处理链表问题时在原链表前加一个虚节点就可以避免对头节点的特殊判断

3、队列

广搜BFS

广度优先搜索一般用来二叉树的层序遍历、求最短路径、最小数量之类的题目。
墙与门
岛屿数量
打开转盘锁
最小跳跃次数

  • 广度优先搜索模板:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    int BFS()
    {
    queue<int> q;
    unordered_set<节点或状态> map; //可选,看是否需要重复入队
    int step = 0;
    q.push(根节点或状态);
    while(!q.empty())
    {
    ++step;
    int size = q.size();
    for(int i = 0; i < size; i++) //遍历当前层所有节点并扩展相邻节点
    {
    节点或状态 cur = q.front(); //取队列头节点
    if(cur == 目标节点或状态) return step;
    for(节点 x : cur的所有相邻节点)
    {
    if(map.count(x)) continue; //判断是否已经访问过,可选
    q.push(x);
    map.insert(x); //可选
    }
    q.pop();
    }
    }
    return step;
    }

4、栈

深搜DFS

一般能用广搜的也能用深搜,但是深搜不能保证是最短路径或者最小数量,深搜还用于二叉树的前中后序遍历。
钥匙和房间
图像渲染
以及上面可以用BFS做的题

  • DFS模板(非递归,类似于上面的BFS)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
bool DFS(节点 root,节点 target)
{
stack<int> s;
unordered_set<节点> visited; //可选,看是否需要重复入栈
s.push(root);
visited.insert(root);
while(!s.empty())
{
节点 cur = q.top(); //取栈顶节点
if(cur == target) return true;
for(节点 x : cur的所有相邻节点)
{
if(visited.count(x)) continue; //判断是否已经访问过,可选
q.push(x);
visited.insert(x); //可选
}
q.pop();
}
return false;
}
  • DFS模板(递归)
1
2
3
4
5
6
7
8
9
10
11
bool DFS(节点 root,节点 target, unordered_set<节点>& visited)
{
if(root == target) return true;
for(节点 x : root的所有相邻节点)
{
if(visited.count(x)) continue; //判断是否已经访问过,可选
visited.insert(x); //可选
if(DFS(x,target,visited)) return true;
}
return false;
}
单调栈

单调栈也是一种重要的数据结构,在实际解题中经常用到,简单来说单调栈内的元素保证是严格单调递增或递减的,如果一个新的元素和栈顶元素不满足这种单调关系,就将栈顶元素出栈并进行一定的操作,直到满足单调关系,将新元素入栈。

之前的题目中也遇到过一些比较简单的单调栈问题,这里以两道困难题目加深对单调栈的理解。

柱状图中最大的矩形

最大矩形

但这两道题较为困难,能够加深对单调栈的理解。柱状图中的最大矩形的思路简单来说就是遍历每一根柱子,分别找到其左边和右边离它最近的高度低于它的柱子作为左右边界,在这两个边界范围内的所有柱子的高度都高于当前柱子,因此所形成的矩形面积就是当前柱子高度乘以边界长度。寻找柱子边界的过程就是利用单调栈实现的,并且优化过后只需要遍历一次就可以找到左边界和右边界。关于单调栈的思路如何得到以及优化具体细节可以查看柱状图中最大的矩形 官方题解

最大矩形是一道更为困难的题目,但是如果理解了柱状图问题,再看最大矩形就不是很困难了。因为我们可以对矩阵每一行的每一个位置都求出这个位置上方连续的1的个数(包含这个位置本身),这样每一行都形成了一个柱状图,再按照柱状图的解法计算最大矩形面积即可,遍历完矩阵所有行也就得到了全局最大矩形。更多细节在代码中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
int m = matrix.size(), n = matrix[0].size();
vector<int> height(n, 0); //记录每一行每个位置上方连续的1的个数(包含本身)
int ans = 0;
for(int i = 0; i < m; ++i)
{
vector<int> left(n), right(n, n); //每一行遍历开始都要重置right数组的初始值为n
//计算每个格子上方连续1的个数,形成柱状图
for(int j = 0; j < n; ++j)
{
if(i == 0)
{
height[j] = matrix[i][j] - '0';
}
else
{
height[j] = matrix[i][j] == '1' ? height[j] + 1 : 0;
}
}
//计算直到当前行的柱状图最大矩形
stack<int> mono;
for(int j = 0; j < n; ++j)
{
while(!mono.empty() && height[mono.top()] >= height[j]) //注意更新右边界的判断条件是'>='
{
right[mono.top()] = j;
mono.pop();
}
left[j] = mono.empty() ? -1 : mono.top();
mono.push(j);
}
//更新结果
for(int j = 0; j < n; ++j)
{
ans = max(ans, height[j] * (right[j] - left[j] - 1));
}
}
return ans;
}
};
---- 本文结束 知识又增加了亿点点!----

文章版权声明 1、博客名称:LycTechStack
2、博客网址:https://lz328.github.io/LycTechStack.github.io/
3、本博客的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系博主进行删除处理。
4、本博客所有文章版权归博主所有,如需转载请标明出处。