【数据结构与算法】常用数据结构(二)

news2025/5/26 7:00:30

😀大家好,我是白晨,一个不是很能熬夜,但是也想日更的人✈。如果喜欢这篇文章,点个赞👍,关注一下👀白晨吧!你的支持就是我最大的动力!💪💪💪

在这里插入图片描述

文章目录

  • 📗前言
  • 📙常用数据结构(一)
    • 🍎单调栈
    • 🍊单调队列
    • 🥭并查集
    • 🍋Trie
    • 🍍哈希表
  • 📘后记

📗前言


大家好,我是白晨。本次又为大家带来的是常用数据结构的模拟实现,主要用于在算法比赛中快速实现一种常用模拟实现。那为什么不用STL呢?首先,STL为了保证其接口的通用性以及要严格符合一个数据结构的定义,在使用时可能不是非常方便;其次,模拟实现的数据结构在运行速度方面是要快于STL的容器的。

上篇文章常用数据结构(一)我们介绍了单链表、双链表、栈、队列以及堆这五种最常用的数据结构的模拟实现,本次白晨将在上篇文章的基础上为大家介绍5种更高级的常用数据结构——单调栈、单调队列、并查集、Trie以及哈希表

由于本次是面向新人的教程,白晨使用大量图片、动图和语言描述详细拆解一个模拟数据结构的实现。如果以前没有接触过并查集、Trie以及哈希表这几类数据结构的同学可以先阅读本篇文章种各个标题下链接的文章。话不多说,我们开始吧。

img


📙常用数据结构(一)


🍎单调栈


单调栈是指栈内元素单调递增或单调递减。具体来说,如果是单调递增栈,那么栈底到栈顶的元素就是从小到大排列的;如果是单调递减栈,那么栈底到栈顶的元素就是从大到小排列的。在使用单调栈时,我们可以利用这个特点来解决一些问题。

单调栈是一种和单调队列类似的数据结构。单调队列主要用于O(n)解决滑动窗口问题,单调栈则主要用于O(n)解决NGE问题(Next Greater Element),也就是,对序列中每个元素,找到下一个比它大的元素。

  • 逻辑结构

febfb1db746d4ac5838975f5fb19cdb2

上图为一个单调递增的栈。

  • 物理结构

数组模拟栈。

image-20230503215659185

  • 具体实现
  1. 初始化

st 为栈,top为栈顶下标。

image-20230503215659185

const int N = 100010;

int st[N], top = 0; // 定义一个数组st和一个变量top,表示栈
  1. 插入

将一个元素插入单调栈时,为了维护栈的单调性,需要在保证将该元素插入到栈顶后整个栈满足单调性的前提下弹出最少的元素。

  1. 如果栈不为空且栈顶元素大于等于插入元素,那么就弹出栈顶元素,直到栈为空或者栈顶元素小于插入元素为止
  2. 将插入元素入栈

例如,栈中自底向上的元素为 { 0, 11, 45, 81 } 。插入元素 14 时为了保证单调性需要依次弹出元素 45, 81 ,操作后栈变为 { 0, 11, 14 } 。

单调栈插入

while (top > 0 && st[top] >= num) top--; // 如果栈不为空且栈顶元素大于等于num,那么就弹出栈顶元素,直到栈为空或者栈顶元素小于num为止
st[++top] = num; // 将num压入栈中

单调栈一般只用插入这个操作,出栈一般在插入的过程中就已经完成。

  • 练习题目

image-20230409104703023

🍬原题链接:单调栈

🪅算法思想

  • 实现一个单调栈即可。

🪆代码实现

#include <iostream>

using namespace std;

const int N = 100010;

int st[N], top = 0; // 定义一个数组st和一个变量top,表示栈

int main()
{
    int n;
    scanf("%d", &n); // 读入一个整数n

    while (n--)
    {
        int num;
        scanf("%d", &num); // 读入一个整数num

        while (top > 0 && st[top] >= num) top--; // 如果栈不为空且栈顶元素大于等于num,那么就弹出栈顶元素,直到栈为空或者栈顶元素小于num为止
        if (top > 0) printf("%d ", st[top]); // 如果栈不为空,那么栈顶元素就是num后面第一个比它小的数
        else printf("-1 "); // 否则就不存在这样的数
        st[++top] = num; // 将num压入栈中
    }
    return 0;
}

🍊单调队列


单调队列是一个限制只能队尾插入,但是可以两端删除的双端队列。单调队列存储的元素值,是从队首到队尾呈单调性的(要么单调递增,要么单调递减)。

但是单调队列和单调栈的功能有重合,所以我们一般不直接使用单调队列,而是使用基于单调队列的衍生数据结构——滑动窗口。下面讲解的数据结构为滑动窗口。

滑动窗口是一种基于双指针的一种思想,两个指针指向的元素之间形成一个窗口。

  • 逻辑结构

滑动窗口

  • 物理结构

数组模拟实现滑动窗口。

  • 具体实现

下面以单调递增队列举例。

  1. 初始化

a存储数据,q为队列,存储数据在a中下标。

head为队头下标,tail为队尾下标,sz为滑动窗口的大小。

初始化队列,将队头指针head赋值为0,将队尾指针tail赋值为-1。

const int N = 1000010;

int a[N], q[N];
int head, tail;
int sz;

注意:滑动窗口q存储的是a中数据的下标,而不是直接存储数据,并且滑动窗口大小指的是覆盖数组a中元素的个数,而不是单调队列的长度。

  1. 入队

滑动窗口的入队操作是从队尾进行插入,保证队列的单调性。具体来说,当要插入一个元素时,

  1. 判断q队头元素,是否已经小于等于当前插入元素在a中的下标isz,如果小于,必须从队头出元素以保证插入这个元素不会超出滑动窗口的范围。
  2. 从队尾开始,将所有比该元素a[i]大的元素出队,直到遇到一个比该元素小的元素或者队列为空。
  3. 将该元素的下标入队。

例如,有a={1,3,-1,-3,5,3,6,7}i为5,此时将a[5] = 3插入sz = 3滑动窗口中。

滑动窗口插入2

// 当队列中的头元素的下标 小于 滑动窗口的下限,出队
if (head <= tail && q[head] < i - k + 1) head++;
// 当队尾元素值 大于 当前元素,此时队尾元素将不再会被输出,所以直接从队尾出队
// 这也是为了满足当前队列的单调性为 单调递增,输出时直接输出队头就是最小的元素
while (head <= tail && a[q[tail]] > a[i]) tail--;
q[++tail] = i;

单调递增的滑动窗口同理:

  1. 判断q队头元素,是否已经小于等于当前插入元素在a中的下标isz,如果小于,必须从队头出元素以保证插入这个元素不会超过滑出窗口的范围。
  2. 从队尾开始,将所有比该元素a[i]小的元素出队,直到遇到一个比该元素大的元素或者队列为空。
  3. 将该元素的下标入队。
  1. 取滑动窗口最值

单调递增队列队头元素为 滑动窗口中最最小值的下标,滑动窗口中最最小值为a[q[head]]

单调递减队列队头元素为 滑动窗口中最最大值的下标,滑动窗口中最最大值为a[q[head]]

int res = a[q[head]];

下面的题目就是滑动窗口最直接的使用,如果你能理解下面的题目,相信你就完全懂了滑动窗口。

  • 练习题目

image-20230409104954744

🍬原题链接:滑动窗口

🪅算法思想

  1. 首先,定义了一个数组a[N],存储数据,以及一个队列q[N],存储数据在a中的下标。
  2. 接着,输入nk,以及n个数据。
  3. 然后,输出滑动窗口的最小值。定义了两个变量headtail,分别表示队列的头和尾。head初始化为0,tail初始化为-1。从0到n-1遍历数组a,每次都进行以下操作:
    • 当队列中的头元素的下标小于滑动窗口的下限时,出队。
    • 当队尾元素值大于当前元素时,此时队尾元素将不再会被输出,所以直接从队尾出队。这也是为了满足当前队列的单调性为单调递增,输出时直接输出队头就是最小的元素。
    • 将当前元素入队。
    • 如果i >= k - 1,则输出a[q[head]]
  4. 最后,输出滑动窗口的最大值。与上面类似。

🪆代码实现

#include <iostream>

using namespace std;

const int N = 1000010;

int a[N], q[N]; // a存储数据,q为队列,存储数据在a中下标
int head, tail;

int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
    
    // 输出滑动窗口最小值
    head = 0, tail = -1;
    for (int i = 0; i < n; ++i)
    {
        // 当队列中的头元素的下标 小于 滑动窗口的下限,出队
        if (head <= tail && q[head] < i - k + 1) head++;
        // 当队尾元素值 大于 当前元素,此时队尾元素将不再会被输出,所以直接从队尾出队
        // 这也是为了满足当前队列的单调性为 单调递增,输出时直接输出队头就是最小的元素
        while (head <= tail && a[q[tail]] > a[i]) tail--;
        q[++tail] = i;
        if (i >= k - 1) printf("%d ", a[q[head]]);
    }
    puts("");
    
    head = 0, tail = -1;
    for (int i = 0; i < n; ++i)
    {
        if (head <= tail && q[head] < i - k + 1) head++;
        while (head <= tail && a[q[tail]] < a[i]) tail--;
        q[++tail] = i;
        if (i >= k - 1) printf("%d ", a[q[head]]);
    }
    puts("");
    return 0;
}

🥭并查集


并查集 (英文:Disjoint-set data structure,直译为不交集数据结构)是一种数据结构 ,用于处理一些不交集 (Disjoint sets,一系列没有重复元素的集合)的合并及查询问题。并查集支持如下操作:

  • 查询:查询某个元素属于哪个集合,通常是返回集合内的一个"代表元素"。这个操作是为了判断两个元素是否在同一个集合之中。

  • 合并:将两个集合合并为一个。

本篇文章只介绍并查集的模拟实现,想具体了解并查集的同学可以参考这篇文章——【数据结构与算法】并查集。

举个例子,有小明、小亮、小虎、小李、小王、小孙六个学生,已知小明小孙是同学,小王小明是同学,小亮小李是同学,小虎小孙是同学。

  • 问:小虎小王是什么关系,小李小王是什么关系?

image-20230130203524181

按照常识,我们可以把互为同学的学生划入同一个集合,如果两个同学的名字在同一个集合中出现,那么这两个人互为同学。反之,两个人不是同学。

image-20230130203119960

观察上图,小虎小王是同学关系,小李小王不是同学关系。

上面就是并查集的简单应用,并查集能够快速合并两个集合以及快速查询两个元素是否在一个集合中,时间复杂度在大量查询的情况下可以达到O(1)

  • 逻辑结构

image-20230130210320997

  • 物理结构

数组模拟实现并查集。

  • 具体实现
  1. 初始化
  • 存储结构:数组
  • 初始化:数组元素全部初始化为-1
  • 下标i:从1号下标开始使用。
  • 存储数据p[i]孩子结点中存放父节点的下标,并查集的元素初始化为自身下标。
const int N = 100010;

int p[N]; 

for (int i = 1; i <= n; ++i) p[i] = i;
  1. 根结点查找

并查集最核心的操作就是查询元素集合的根,如果两个元素集合的根相同,说明两个元素在同一个集合中。子节点存放的是父节点的下标,只需要向上查找就能找到根。

  1. 如果当前节点不是根节点,就递归地找到它的父节点,然后将它的父节点指向根节点。这样可以压缩路径,使得每个节点都直接指向根节点,从而提高了查找效率。
  2. 如果当前节点是根节点,直接返回自己的下标。
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}
  1. 合并

将两个集合合并:

  1. 查找两个要合并元素的根节点,根节点相同则不用合并;
  2. 如果两个根节点不同,则将随便将其中一个集合的根节点连接到另一个集合根节点下。
void merge(int x, int y)
{
    p[find(x)] = find(y);
}
  • 相关题目

image-20221231191847308

🍬原题链接:合并集合

🪅算法思想

并查集基本实现 + 应用。

🪆代码实现

// 模板并查集
#include <iostream>

using namespace std;

const int N = 100010;

int p[N];

// 根结点查找 + 路径优化
int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

void merge(int x, int y)
{
    p[find(x)] = find(y);
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) p[i] = i;

    while (m--)
    {
        char op[2];
        int x, y;
        scanf("%s%d%d", op, &x, &y);

        if (op[0] == 'M') merge(x, y);
        else
        {
            if (find(x) == find(y)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

🍋Trie


Trie字典树又叫前缀树(prefix tree),用以较快速地进行单词或前缀查询。Trie树本质上就是一棵多叉树,用来存储字符串或者其他数据。

本篇文章只介绍Trie的模拟实现,想具体了解Trie的同学可以参考这篇文章——【数据结构与算法】Trie_

  • 逻辑结构

  • 物理结构

数组模拟实现Trie。

  • 具体实现
  1. 初始化
  • Trie:利用类似于单链表的方式模拟树形结构,Trie[i]就是一个结点,而Trie[i][26]为每个节点存储的子节点下标,相当于静态节点的26个子节点指针
  • cnt:统计以Trie树的第N个结点所代表的字母结尾的单词数量
  • idx:与单链表的idx类似,由于Trie树是利用二维数组模拟的,每一个Trie[i]为一个结点
const int N = 100010;

int Trie[N][26];
int cnt[N]; 
int idx = 0;
  1. 插入

将字符串s插入到Trie中:

  1. 从根节点开始遍历字符串的每个字符;
  2. 如果该字符没有被插入到当前结点下,就将其插入(启用一个新节点,将新节点的下标保存到父节点),然后跳转到子节点,从子节点继续插入。
  3. 最后,以下标为 p 结点结尾的单词数量加 1。
void insert(const string& s)
{
    int p = 0; // 从头结点开始遍历
    for (int i = 0; i < s.size(); ++i)
    {
        int pos = s[i] - 'a'; // 下标映射
        // 如果该字母没有被插入到当前结点下,将其插入(启用一个新节点,将新节点的下标保存到父节点)
        // 当Trie[p][pos] 的值为0时,代表当前位置没有子节点
        if (!Trie[p][pos]) Trie[p][pos] = ++idx;
        p = Trie[p][pos]; // 跳转到子节点,从子节点继续插入
    }
    cnt[p]++; // 以下标为p结点结尾的单词数量加1
}
  1. 查询

查询字符串s是否在Trie中出现过:

  1. 从根节点开始遍历字符串的每个字符;
  2. 如果该字符没有被插入到当前结点下,说明当前字母查找失败;
  3. 否则,继续查找。最后返回以下标为 p 结点结尾的单词数量。
int query(const string& s)
{
    int p = 0;
    for (int i = 0; i < s.size(); ++i)
    {
        int pos = s[i] - 'a';
        // 当Trie[p][pos] 的值为0时,代表当前位置没有子节点,也说明当前字母查找失败
        if (!Trie[p][pos]) return 0;
        p = Trie[p][pos]; // 有子节点,继续查找
    }
    return cnt[p];
}
  • 练习题目

image-20221226201219977

🍬原题链接:Trie字符串统计

🪅算法思想

按照字典树的结构进行插入和查询即可,主要注意实现。

具体实现见下面代码。

🪆代码实现

#include <iostream>
#include <string>

using namespace std;

const int N = 100010;

int Trie[N][26]; // Trie树,利用类似于单链表的方式模拟树形结构,Trie[i]就是一个结点,而Trie[i][26]为每个节点存储的子节点下标,相当于静态节点的26个子节点指针
int cnt[N]; // 统计以Trie树的第N个结点所代表的字母结尾的单词数量
int idx = 0; // 与单链表的idx类似,由于Trie树是利用二维数组模拟的,每一个Trie[i]为一个结点
// 所以要让二维数组表示出树形关系,就得让父节点指向子节点的下标,idx代表当前使用到了哪一个结点,每使用一个结点,idx++。
// 初始除了头结点(Trie[0])以外,其他结点都没有使用,所以idx = 0。
int n;

void insert(const string& s)
{
    int p = 0; // 从头结点开始遍历
    for (int i = 0; i < s.size(); ++i)
    {
        int pos = s[i] - 'a'; // 下标映射
        // 如果该字母没有被插入到当前结点下,将其插入(启用一个新节点,将新节点的下标保存到父节点)
        // 当Trie[p][pos] 的值为0时,代表当前位置没有子节点
        if (!Trie[p][pos]) Trie[p][pos] = ++idx;
        p = Trie[p][pos]; // 跳转到子节点,从子节点继续插入
    }
    cnt[p]++; // 以下标为p结点结尾的单词数量加1
}

int query(const string& s)
{
    int p = 0;
    for (int i = 0; i < s.size(); ++i)
    {
        int pos = s[i] - 'a';
        // 当Trie[p][pos] 的值为0时,代表当前位置没有子节点,也说明当前字母查找失败
        if (!Trie[p][pos]) return 0;
        p = Trie[p][pos]; // 有子节点,继续查找
    }
    return cnt[p];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cin >> n;

    while (n--)
    {
        string op, s;
        cin >> op >> s;
        if (op == "I") insert(s);
        else cout << query(s) << endl;
    }
    return 0;
}

🍍哈希表


**哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度,哈希表的增删查改操作都是O(1)。**这个映射函数叫做散列函数,存放记录的数组叫做散列表。

给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

本篇文章只介绍哈希的模拟实现,想具体了解哈希的同学可以参考这篇文章——【算法】哈希表

  • 两种结构
  1. 开散列(拉链法)

image-20230106151146732

  1. 闭散列(开放寻址法)image-20230106151211779

这两种实现的区别在于发生哈希冲突(关键值根据哈希函数得到的映射位置相同)以后处理的方式不同,拉链法是将冲突的元素全部在一个位置上串起来,像一个拉链一样;而开放寻址法是通过再哈希,确定一个没有值使用新的位置,保证一个位置只存放一个值。

  • 开散列具体实现
  1. 初始化

哈希表的大小一般为质数,减少哈希冲突。

  • h:哈希表,对应位置存储单链表下标

  • e:存储数据值。

  • ne:存储该节点的下一个节点的坐标。

  • idx:指向ene中下一个要使用的节点。

  • e,ne,idx:单链表,模拟每个哈希结点下挂的拉链。这里要注意上面的模拟单链表不是一般意义上的单链表,而是用数组模拟了多个单链表,用ne[k] = -1表示 NULL。ne[k] = -1时,表示k结点没有后驱结点了,也就是一个单链表结束。

const int N = 100003; // 超过10w的最小质数

int h[N]; 
int e[N], ne[N], idx; 
  1. 插入

将数据插入哈希表:

先计算出 x 的哈希值 k,然后将 x 插入到以 h[k] 为头结点的单链表中。

void insert(int x)
{
    int k = (x % N + N) % N; // 保证模出来的数一定为正数
    // 单链表头插
    e[idx] = x;
    ne[idx] = h[k];
    h[k] = idx++;
}
  1. 查询

查询数据是否在哈希表中:

先计算出 x 的哈希值 k,然后遍历以 h[k] 为头结点的单链表,查找是否存在 x

bool find(int x)
{
    int k = (x % N + N) % N;
    for (int i = h[k]; i != -1; i = ne[i])
        if (e[i] == x)
            return true;
    return false;
}
  • 闭散列具体实现
  1. 初始化
  • null:表示此位置为空

  • h:哈希表,存数据。

开散列法时间复杂度主要取决于冲突次数,所以将数组大小开成要求大小的2~3倍。

const int N = 200003, null = 0x3f3f3f3f; 

int h[N]; 
  1. 查询

查询数据是否在哈希表中,查询成功返回下标,失败返回该数应该被插入的下标:

先计算出 x 的哈希值 k,然后从 h[k] 开始往后遍历数组,直到找到 x 或者遇到空位置为止。

int find(int x)
{
    int k = (x % N + N) % N;

    // 不考虑数组被占满的情况
    while (h[k] != null && h[k] != x)
    {
        k++;
        if (k == N) k = 0;
    }
    return k;
}
  1. 插入

将数据插入哈希表中:

先调用 find 函数查找 x 是否已经存在,如果不存在,则将 x 插入到 find 函数返回的下标处。

void insert(int x)
{
    int k = find(x);
    if (h[k] == null) h[k] = x;
}
  • 练习题目

image-20230106132559736

🍬原题链接:模拟散列表

🪅算法思想

按照哈希表的思想进行实现,主要看下文代码实现。

🪆代码实现

  • 拉链法(开散列法)
// 拉链法
#include <iostream>
#include <cstring>

using namespace std;

const int N = 100003; // 超过10w的最小质数

int h[N]; // 哈希表,对应位置存储单链表下标
int e[N], ne[N], idx; // 单链表,模拟每个哈希结点下挂的拉链
// 这里要注意上面的模拟单链表不是一般意义上的单链表,而是用数组模拟了多个单链表,用ne[k] = -1表示 NULL
// ne[k] = -1时,表示k结点没有后驱结点了,也就是一个单链表结束

void insert(int x)
{
    int k = (x % N + N) % N; // 保证模出来的数一定为正数
    // 单链表头插
    e[idx] = x;
    ne[idx] = h[k];
    h[k] = idx++;
}

bool find(int x)
{
    int k = (x % N + N) % N;
    for (int i = h[k]; i != -1; i = ne[i])
        if (e[i] == x)
            return true;
    return false;
}

int main()
{
    int m;
    scanf("%d", &m);
    memset(h, 0xff, sizeof h);

    while (m--)
    {
        char op[2];
        int x;
        scanf("%s%d", op, &x);

        if (op[0] == 'I') insert(x);
        else
        {
            if (find(x)) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}
  • 开放寻址法(闭散列法)
// 开放寻址法
#include <iostream>
#include <cstring>

using namespace std;

const int N = 200003, null = 0x3f3f3f3f; // null表示这个位置为空

int h[N]; // 开散列法时间复杂度主要取决于冲突次数,所以将数组大小开成要求大小的2~3倍

// 查找成功返回下标,失败返回该数应该被插入的下标
int find(int x)
{
    int k = (x % N + N) % N;

    // 不考虑数组被占满的情况
    while (h[k] != null && h[k] != x)
    {
        k++;
        if (k == N) k = 0;
    }
    return k;
}

void insert(int x)
{
    int k = find(x);
    if (h[k] == null) h[k] = x;
}

int main()
{
    memset(h, 0x3f, sizeof(h));
    int m;
    scanf("%d", &m);

    while (m--)
    {
        char op[2];
        int x;
        scanf("%s%d", op, &x);

        if (op[0] == 'I') insert(x);
        else
        {
            int k = find(x);
            if (h[k] != null) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}


📘后记


本篇文章的数据结构非常重要,不仅是算法题目中最常用的几种结构,更是以后图论等算法的基础。希望大家能够将本篇文章和常用数据结构(一)中的数据结构模板牢牢记住,做到随用随写。

如果讲解的有不对之处还请指正,我会尽快修改,多谢大家的包容。

如果大家喜欢这个系列,还请大家多多支持啦😋!

如果这篇文章有帮到你,还请给我一个大拇指 👍和小星星 ⭐️支持一下白晨吧!喜欢白晨【算法】系列的话,不如关注👀白晨,以便看到最新更新哟!!!

我是不太能熬夜的白晨,我们下篇文章见。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/501237.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

asp.net+sqlserver基于web的奖学金助学金评定系统

管理员部分功能 管理员管理&#xff0c;管理系统内的所有管理人员信息 1.学生信息管理&#xff0c;管理系统内需要进行奖助学金评定的学生信息 2.教师信息管理&#xff0c;管理学院内的所有教师信息 3.一级指标管理&#xff0c;管理奖助学金评定过程中的一级指标内容 4.二级指标…

IPC:匿名管道和命名管道

一 管道初级测试 写两个小程序&#xff0c;一个负责向管道发数据&#xff0c;一个从管道接收数据&#xff1b; pipe.cpp #include <iostream> using namespace std;int main() {cout << "hello world" << endl;return 0; } pipe2.cpp #inclu…

【LeetCode】环形链表+结论证明

题目链接&#xff1a;环形链表 题目&#xff1a;给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 p…

中级软件设计师备考--解答题--数据流图

目录 基本概念基本元素数据流图的分层平衡原则 数据字典 基本概念 数据流图也称为数据流程图&#xff08;DFD&#xff09;&#xff0c;它摆脱了系统的物理内容&#xff0c;精确地在逻辑上描述系统的功能、输入、输出和数据存储&#xff0c;是系统逻辑模型的重要组成部分。 基…

《走进对象村3》找对象送孩子之特殊的构造方法

文章目录 &#x1f680;文章导读1. 构造方法1.1 构造方法的分类1.1.1 非默认的静态方法1.1.2 默认的构造方法1.1.3 带参数的构造方法 构造方法的特性&#xff1a; &#x1f680;文章导读 在本篇文章中&#xff0c;对构造方法进行了一些总结&#xff0c;可能也有不足的地方&…

Golang每日一练(leetDay0059) 两数之和II、Excel表列名称

目录 167. 两数之和 II 输入有序数组 Two-sum-ii-input-array-is-sorted &#x1f31f;&#x1f31f; 168. Excel表列名称 Excel Sheet Column Title &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练…

MySQL多表查询之连接查询

0. 数据源 /*Navicat Premium Data TransferSource Server : localhost_3306Source Server Type : MySQLSource Server Version : 80016Source Host : localhost:3306Source Schema : tempdbTarget Server Type : MySQLTarget Server Version…

《走进对象村6》面向对象的第三大特性——多态

文章目录 &#x1f680;文章导读1.1 多态的概念1.2 多态的实现条件1.3 向上转型和向下转型1.4 重写 **面试问题&#xff1a;重写和重载的区别**多态的实现 &#x1f680;文章导读 在本篇文章中&#xff0c;将会有很多的干货和知识点掌握&#xff0c;希望读者慢慢耐心阅读 在本篇…

如何设定项目中的里程碑?

项目管理中非常重要的就是合理设置阶段性的里程碑&#xff0c;在项目实施过程中&#xff0c;根据里程碑来灵活控制项目进度和节奏。那么一个IT项目该如何合理地安排里程碑呢&#xff1f; 在IT项目管理中&#xff0c;里程碑是一种非常重要的工具&#xff0c;它能够帮助项目经理和…

谈谈架构分层

大家好&#xff0c;我是易安&#xff01; 在系统从0到1的阶段&#xff0c;为了让系统快速上线&#xff0c;我们通常是不考虑分层的。但是随着业务越来越复杂&#xff0c;大量的代码纠缠在一起&#xff0c;会出现逻辑不清晰、各模块相互依赖、代码扩展性差、改动一处就牵一发而动…

IIS6.0和网络管理软件SNMPc

一.IIS6.0 IIS 6.0提供了更为方便的安装/管理功能和增强的应用环境、基于标准的分布协议、改进的性能表现和扩展性&#xff0c;以及更好的稳定性和易用性。其服务组件包括&#xff1a; ①WWW服务。WWW是图形最为丰富的Internet服务。Web具有很强的链接能力&#xff0c;支持协…

软考中级——系统集成项目管理工程师(20天冲刺)

PV:计划价值(计划要成的价值) EV:挣值(实际做了的事儿的价值) AC:实际成本(实际花出去多少钱) SV:进度偏差EV-PV(项目提前或者落后的进度)>0项目进度超前<0项目进度落后 CV:成本偏差EV-AC(项目预算的号空成者盈利)>0成本节约<0成本超支 SPI:进度绩效指数EV/PV(挣值…

创建VUE2 前端以及后端的交互

创建vue2项目 1.javascript–>vue(不要勾选)–>安装element-ui(组件 | Element)–>执行指令&#xff08;npm i element-ui -S&#xff09;–>在main.js中引入&#xff08;import ElementUI from ‘element-ui’; ​ import ‘element-ui/lib/theme-chalk/index.c…

JavaWeb:Web 的基本概念、Tomcat 服务器、Http 详解、Maven 的下载安装步骤、模仿一个 Servlet

文章目录 JavaWeb - 01一、基本概念1、静态 Web2、动态 Web3、Web 应用程序4、三个技术 二、Web 服务器三、Tomcat 详解四、发布一个 Web 网站五、Http 详解1. Http 请求&#xff08;1&#xff09;请求行&#xff08;2&#xff09;消息头 2. Http 响应&#xff08;1&#xff09…

sourceTree离线环境部署

目录 1、下载sourceTree安装包&#xff0c;打开之后弹出注册界面&#xff08;需要去国外网站注册&#xff09;2、使用技术手段跳过注册步骤3、打开安装包进行安装 注&#xff1a;建议提前安装好git 1、下载sourceTree安装包&#xff0c;打开之后弹出注册界面&#xff08;需要去…

27 - 两数、三数、四数问题

文章目录 1. 两数之和2. 三数之和3. 最接近的三数之和4. 四数之和5. 四数相加 1. 两数之和 在遍历数组的时候只需要在map中去查询是否有个目前元素target - numbers[i]匹配的数值&#xff0c;如果有&#xff0c;就找到匹配对&#xff0c;如果没有就把目前遍历的元素放入map中&a…

融合有序,创造无限——解密力扣“合并两个有序数组”

本篇博客会讲解力扣“88. 合并两个有序数组”这道题&#xff0c;这是题目链接。 其实&#xff0c;有经验的朋友一看到这个题目的名字&#xff0c;应该就明白了&#xff0c;这玩意和归并排序脱不了干系。下面我们来审题&#xff1a; 输出示例如下&#xff1a; 以下是一些提…

3.是人就能学会的Spring源码教学-IOC容器的核心实现原理

是人就能学会的Spring源码教学-IOC容器的核心实现原理 我们学习Spring源码的动力&#xff0c;估计大部分人都是因为面试中会问到吧。 那么我们今天就以面试问Spring来开头。 关于Spring&#xff0c;在面试的时候一般会问到的两个最基础的问题。 第一个什么是IOC&#xff1f…

技术控,看这里,一款支持断点调试的数据科学工具

数据科学是一门利用统计学、机器学习、数据挖掘、数据可视化等技术和方法&#xff0c;从数据中提取知识和信息的交叉学科。自上世纪60年代&#xff0c;统计学家John W.Tukey首次提出“数据分析”&#xff08;Data Analysis&#xff09;的概念起&#xff0c;数据科学已历经了几十…

ASEMI代理ADUM131E1BRWZ-RL原装ADI车规级ADUM131E1BRWZ-RL

编辑&#xff1a;ll ASEMI代理ADUM131E1BRWZ-RL原装ADI车规级ADUM131E1BRWZ-RL 型号&#xff1a;ADUM131E1BRWZ-RL 品牌&#xff1a;ADI /亚德诺 封装&#xff1a;SOIC-16-300mil 批号&#xff1a;2023 安装类型&#xff1a;表面贴装型 引脚数量&#xff1a;16 工作温度…