【C++】手把手教你看懂的 STL map 详解(超详细解析,小白一看就懂!!)

news2024/11/13 9:42:13

目录

一、前言

二、预备知识  

💢关联式容器💢 

💢键值对💢  

💢哈希结构的关联式容器💢  

三、map 详解   

🔥map 的介绍  

🔥map的模板参数说明 

🔥map的构造函数 

🔥map的使用 

🍇 insert

🍐 operator [ ] 

🥝 find 

🍍 erase

🍉 swap

🍈 empty

🍌 size 

🍋 count 

🔥 总结

四、常考面试题 

五、共勉 


一、前言

【map】  是 STL 中的容器之一,不同于普通容器,它的查找速度极快,常用来存储各种经常被检索的数据,因为容器的底层是红黑树。除此之外,还可以借助其特殊的性质,解决部分难题。

二、预备知识  

在正式学习 map 之前,首先要有一些预备知识,否则后面可能看不懂相关操作 

💢关联式容器💢 

在以往的 【STL 容器学习中,我们接触到的都是 序列式容器,比如 stringvectorlistdeque 等,序列式容器的特点就是 底层为线性序列的数据结构,就比如 list,其中的节点是 线性存储 的,一个节点存储一个元素,其中存储的元素都可序,但未必有序  

  • 关联式容器 则比较特殊,其中存储的是 <key, value> 的 键值对,这就意味着可以按照 键值大小 key 以某种特定的规则放置于适当的位置,关联式容器 没有首尾的概念,因此没有头插尾插等相关操作,本文中学习的 map 就属于 关联式容器 

注意: stackqueue 等适配器也属于序列式容器,因为他们的底层是 deque 等容器  


💢键值对💢  

键值对】是 一种用来表示具有一一对应关系的结构,该结构中一般只包含两个成员变量:key 和 value,前者表示 键值,后者表示 实值 关联式容器的实现离不开【键值对

  • 因此在标准库中,专门提供了这种结构  pair 定义如下 :  
//SGI 版 STL 中的实现
template <class T1, class T2>
struct pair {
  typedef T1 first_type;
  typedef T2 second_type;
 
  T1 first;	
  T2 second;	
  pair() : first(T1()), second(T2()) {}
  pair(const T1& a, const T2& b) : first(a), second(b) {}
 
#ifdef __STL_MEMBER_TEMPLATES
  template <class U1, class U2>
  pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}
#endif
};
  • pair 中的 first 表示 键值second 则表示 实值,在给 【关联式容器】 中插入数据时,可以构建 pair 对象 

 比如下面就构建了一个 键值 key 为 string实值 value 为 int 的匿名 键值对 pair 对象  

pair<string, int>("hehe", 123);
  • 可以将此匿名对象传入 关联式容器 中,当然这样写未免过于麻烦了,于是库中设计了一个函数模板 make_pair,可以根据传入的参数,去调用 pair 构建对象并返回
make_pair("hehe", 123);	//构建出的匿名对象与上面的一致

make_pair 的定义如下所示:   

template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)
{
  return ( pair<T1,T2>(x,y) );
}
  • 该函数实际会被编译器优化为 内联函数,因此不会造成过多消耗,可以放心使用 

💢哈希结构的关联式容器💢  

 所以在 C++ 标准中,共提供了四种 哈希结构的关联式容器  

  • unordered_set
  • unordered_multiset
  • unordered_map
  • unordered_multimap

关于 树形结构的关联式容器 将在 二叉搜索树 中学习

树型结构与哈希结构的关联式容器功能都是一模一样的,不过 哈希结构查找比树型结构快得多 -> O(1)

注:

  • STL 中选择的树型结构为 红黑树 RB-Tree
  • 树型结构中的元素 中序遍历 后有序,而哈希结构中的元素无序

三、map 详解   

在C++98中,STL提供了底层为 红黑树结构 的一系列关联容器,在查询时效率可以达到 log(N)。但是在较差的情况下 ,需要比较红黑树的高度次,当树中节点非常多的时候,查询效率也会不理想达到 log(N)

  • 接下来我们将会对,map 进行详细的介绍,其余的容器将会在后续的文章中讲述。 

🔥map 的介绍  

map是关联容器,它按照特定的次序(按照key来比较)存储 由键值 key 和值 value 组合而成的元素。

  • 在map中,键值 key 通常用于排序和唯一的标识元素,而 value 中存储的值与键值 key 产生关联。键值 key 和值 value 的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起, 为其取别名称为pair::typedef pair value_type;
  • 在内部,map中的元素总是按照键值key进行比较排序的。
  • map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行 直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  • map支持下标访问符,即在 [ ] 中放入key,就可以找到与key对应的value。
  • map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))


🔥map的模板参数说明 

 如下图所示:

  • key:  键值对中 key 的类型       
  • T键值对中 value 的类型 
  • Compare:  比较器的类型,map 中的元素是按照 key 来比较的,缺省情况下按照小于来比较,一般情况 下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则 (一般情况下按照函数指针或者仿函数来传递) 
  • Alloc通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器 

注意:在使用map时,需要包含头文件。 

#include <map>

🔥map的构造函数 

 主要有下面三个构造方式:

(1)指定 key 和 value 的类型构造一个空容器 

// 构造一个key为string类型,value为int类型的空容器
map<string, int> m1;

(2)拷贝构造某类型容器 

// 拷贝构造key为string类型,value为int类型的m1容器的复制品
map<string, int> m2(m1); 

(3)使用迭代器区间进行初始化构造 

// 使用迭代器拷贝构造m2容器某段区间的复制品
map<string, int> m3(m2.begin(), m2.end());

简单的使用一下: 

#include <iostream>
#include <vector>
#include <map>
using namespace std;

int main()
{
	vector<pair<string, int>> arr = { make_pair("G", 71), make_pair("A", 65), make_pair("F", 70) };

	map<string, int> m1;	//创建一个空的 map
	map<string, int> m2(arr.begin(), arr.end());	//创建包含数据的 map

	cout << "m1: " << endl;
	for (auto e : m1)
		cout << e.first << " | " << e.second << endl;
	cout << "========================" << endl;
	cout << "m2: " << endl;
	for (auto e : m2)
		cout << e.first << " | " << e.second << endl;

	return 0;
}


🔥map的使用 

map 的接口虽然比较多,但是常用的也就那么几个 

🍇 insert

在 map 中插入键值对 x ,注意 x 是一个键值对,返回值也是键值对 

  • iterator 代替新插入元素的位置,bool 代表是否插入成功 

  • 在 map 的内部,key 与 value 通过成员类型 value_type 绑定在一起,为其取别名称为 pair 
typedef pair<const key, T> value_type;
  •  下面对于 insert 的插入有两种方式

(1)直接使用 pair 直接来构建键值对 

void testmap()
{
	map<string, string> dict;

	// 调用pair的构造函数,构造一个匿名对象插入
	dict.insert(pair<string, string>("sort", "排序"));
	dict.insert(pair<string, string>("root", "根"));
	dict.insert(pair<string, string>("left", "左边"));

	// 遍历
	for (auto e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}
}
  •  可以看到插入的英文是按照升序进行排列的。

(2)使用 make_pair 函数来构造键值对 

  • 构造一个第一个元素设为 x ,第二个元素设为 y 的 pair 对象
  • 模板类型可以传递给  make_pair 的参数隐式推到出来
  • 如果各自类型可以隐式转换,则可以从包含不同类型的其它 pair 对象构造 pair 对象 

void testmap()
{
	map<string, string> dict;

	// 调用make_pair的构造函数,构造一个匿名对象插入
	dict.insert(make_pair("sort", "排序"));
	dict.insert(make_pair("root", "根"));
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("up", "上面"));

	// 遍历
	for (auto e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}
}
  • 推荐使用这个方式插入数据 

(3)统计水果出现的次数

既然已经知道 insert 的用法,我就用 map 来统计一下水果出现的次数   

void testmap()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

	map<string, int> countFruit;
	for (auto& str : arr)
	{
		map<string, int>::iterator it = countFruit.find(str);
		if (it != countFruit.end())
		{
			it->second++;
		}
		else
		{
			countFruit.insert(make_pair(str, 1));
		}
	}

	// 遍历
	for (const auto& kv : countFruit)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}
  •  可以看出 水果的次数已经被打印出来啦

但是代码还是可以进行优化,我们知道 insert 的返回值是 pair<iterator,bool>

其中第一个成员 iterator (pair::first)是指向新插入元素或映射中具有等效键的元素迭代器。

第二个成员 bool (pair::second)是返回插入成功与否的结果

  • 若待插入元素的键值 key 在 map 当中不存在,则 insert 函数插入成功,并返回插入后元素的迭代器和 true
  • 若待插入元素的键值 keymap 当中已经存在,则 insert 函数插入失败,并返回 map 当中键值为 key 的元素的迭代器和 false。  
void testmap()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

	map<string, int> countFruit;
	for (auto& str : arr)
	{
		pair<map<string, int>::iterator, bool> ret = countFruit.insert(make_pair(str, 1));
		// auto ret = countFruit.insert(make_pair(str, 1)); // 也可以写成auto
		if (ret.second == false)
		{
			ret.first->second++; // ret.first是插入位置的迭代器,通过迭代器去访问second
		}
	}

	// 遍历
	for (const auto& kv : countFruit)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}
  • 运行可以看到结果是一样的


🍐 operator [ ] 

返回 key 对应的 value :  

  • 如果 k 与容器中元素的键匹配,则函数返回对其映射值得引用。
  • 如果 k 与容器中任何元素得键不匹配,该函数将插入一个具有该键得新元素,并返回对其映射值得引用。 

对这个函数的调用相当于:

(*((this->insert(make_pair(k,mapped_type()))).first))

operator[ ] 的原理就是:

  • <key,T()> 构造一个键值对,然后调用 insert() 函数将该键值对插入到 map 中
  • 如果 key 已经存在,插入失败,insert 函数返回该 key 所在位置的迭代器
  • 如果 key 不存在,插入成功,insert 函数返回新插入元素所在位置的迭代器
  • operator[ ] 函数最后将 insert 返回值键值对中的 value 返回  
void testmap()
{
	map<string, string> dict;
	
	// 用make_pair函数来构造键值对
	dict.insert(make_pair("sort", "排序"));
	dict.insert(make_pair("root", "根"));
	dict.insert(make_pair("left", "左边"));
	
	dict["up"] = "向上"; // up不存在,那么就插入up元素并修改(插入+修改)
	dict["left"] = "剩余"; // left存在,那么只修改(查找+修改)
	dict["erase"]; // erase不存在,那么插入
	
	// 遍历
	for (auto e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}
}

  • 对于上面统计水果的次数,其实比较常用的就是 operator[ ] 
void testmap()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };

	map<string, int> countFruit;
	for (auto& str : arr)
	{
		countFruit[str]++; 
	}

	// 遍历
	for (const auto& kv : countFruit)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}

  • 如果水果是第一次出现,那么就先插入,因为是第一次出现, 所以水果的次数为 0,插入成功以后,会把插入的这个水果所在的节点里面的次数进行引用返回,也就是返回 0 的引用,那么此时进行++,变成 1 。
  • 如果水果是第二次出现,那么不会插入成功,那么就直接返回这个水果所在的节点迭代器里面的次数,然后再进行++变成 2。 

注意:这里的 [ ] 不支持随机访问 !


🥝 find 

在容器中搜索键值等于 k 的元素,如果找到则返回该元素的迭代器,否则返回map::end的迭代器。

void testmap()
{
	map<string, string> dict;
	
	// 用make_pair函数来构造键值对
	dict["sort"] = "排序";
	dict["up"] = "向上";;
	dict["left"] = "左边";
	dict["root"] = "根";
	
	auto pos = dict.find("root");
	if (pos != dict.end())
	{
		cout << "找到了" << endl;
		cout << pos->first << ":" << pos->second << endl;
	}
}


🍍 erase

从 map 容器中移除单个元素或一组元素

 (1)从 map 容器中移除单个元素

void testmap()
{
	map<string, string> dict;

	// 用make_pair函数来构造键值对
	dict["sort"] = "排序";
	dict["up"] = "向上";;
	dict["left"] = "左边";
	dict["root"] = "根";

	auto pos = dict.find("root");
	if (pos != dict.end())
	{
		dict.erase(pos);
		cout << "删除成功" << endl;
	}

	// 遍历
	for (auto e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}
}

(2)从 map 容器中移除一组元素(【first,last)) 

void testmap()
{
	map<string, string> dict;

	// 用make_pair函数来构造键值对
	dict["sort"] = "排序";
	dict["up"] = "向上";;
	dict["left"] = "左边";
	dict["root"] = "根";

	auto pos = dict.find("root");
	if (pos != dict.end())
	{
		dict.erase(pos, dict.end()); // 删除从pos位置开始后面所有的元素
		cout << "删除成功" << endl;
	}

	// 遍历
	for (auto e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}
}
  •  可以看到 root 后面的元素已经删掉了


🍉 swap

交换 map 容器中的元素 

void testmap()
{
	map<string, string> dict1;
	dict1["sort"] = "排序";
	dict1["up"] = "向上";;
	dict1["left"] = "左边";
	dict1["root"] = "根";

	map<string, string> dict2;
	dict2["size"] = "大小";
	dict2["erase"] = "删除";
	dict2["clear"] = "清除";
	dict2["insert"] = "插入";

	dict1.swap(dict2); // 交换两个对象中的元素

	for (auto e1 : dict1)
	{
		cout << e1.first << ":" << e1.second << endl;
	}
	cout << endl;

	for (auto e2 : dict2)
	{
		cout << e2.first << ":" << e2.second << endl;
	}
}
  • 可以看到两个对象的元素已经被交换 


🍈 empty

检测 map 中的元素是否为空,是返回 true,否则返回 false 

void testmap()
{
	map<string, string> dict1;
	dict1["sort"] = "排序";
	dict1["up"] = "向上";
	dict1["left"] = "左边";
	dict1["root"] = "根";

	map<string, string> dict2;

	cout << dict1.empty() << endl; // 不为空,返回false

	cout << dict2.empty() << endl; // 为空,返回true
}


🍌 size 

返回 map 中的有效元素个数 

void testmap()
{
	map<string, string> dict;
	dict["sort"] = "排序";
	dict["up"] = "向上";
	dict["left"] = "左边";
	dict["root"] = "根";

	cout << dict.size() << endl;
}


🍋 count 

获取 map 容器中指定 k 值的元素个数 

void testmap()
{
	map<string, string> dict;
	dict["sort"] = "排序";
	dict["up"] = "向上";
	dict["left"] = "左边";
	dict["root"] = "根";

	cout << dict.count("root") << endl; // root的个数
	cout << dict.count("hello") << endl; // hello的个数
}

  • 这个接口对于 map 容器其实没有太大用处,因为 map 当中的每个键值 key 和值 value 都是唯一的。 

🔥 总结

  1. map 中的元素是键值对
  2. map 中的 key 是唯一的,并且不能修改
  3. 默认按照小于的方式对 key 进行比较
  4. map 中的元素如果用迭代器去遍历,可以得到一个有序的序列
  5. map 的底层为平衡搜索树(红黑树),查找效率比较高 O(logN)
  6. 支持 [ ] 操作符,operator[ ] 中实际进行插入查找

四、常考面试题 

题目描述:前 K 个高频单词 
题目链接:
692. 前K个高频单词 - 力扣(LeetCode)

  • 利用 map 建立 <string, int> 的映射关系,在按照字典序排序的同时统计出每个单词的出现频率,再通过快排依照数量进行二次排序,选择前 k 个高频单词即可 
  • 只需要将 仿函数进行设计即可:优先按照出现频率排序,如果频率相同,则按照字典序排序即可 
//map + sort
class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        //统计每个单词出现的频率,同时先按照字典序排序
        map<string, int> table;
        for(auto e : words)
            table[e]++;

        //将处理好的数据存入数组中
        vector<pair<string, int>> vTable(table.begin(), table.end());

        //按照出现频率进行二次排序
        sort(vTable.begin(), vTable.end(), 
        [](const pair<string, int>& kv1, const pair<string, int>& kv2)->bool
        {
            return kv1.second == kv2.second ? kv1.first < kv2.first : kv1.second > kv2.second;
        });

        //取出前 k 个高频单词
        vector<string> vs;
        for(int i = 0; i < k; i++)
            vs.push_back(vTable[i].first);
        
        return vs;
    }
};

五、共勉 

以下就是我对 【C++】map 容器 的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新【C++】请持续关注我哦!!!  

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

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

相关文章

HarmonyOS应用开发( Beta5.0)HOS-用户认证服务:面部识别

介绍 User Authentication Kit&#xff08;用户认证服务&#xff09;提供了基于用户在设备本地注册的人脸和指纹来认证用户身份的能力。 用户向应用/系统服务请求访问某些个人数据或执行某些敏感操作时&#xff0c;应用/系统服务将调用系统用户身份认证控件对用户身份进行认证…

AI在医学领域:MIL回归用于前列腺癌复发预测

2024年&#xff0c;全球男性新癌症病例预计为1029080例&#xff0c;其中前列腺癌病例预计为29%。前列腺癌是男性中第二常见的癌症类型&#xff0c;仅次于肺癌。它主要影响老年男性&#xff0c;且发病率随年龄增长而增加。前列腺癌的主要治疗方法是前列腺切除术&#xff0c;但术…

知识竞赛答题软件应用场景有哪些

知识竞赛答题软件应用常见场景有哪些&#xff1f; 一、场景分析&#xff1a;该答题软件基于java技术和原生小程序开发完成&#xff0c;其功能主要包括&#xff1a;个人答题、好友pk、排位pk升级赛、专题pk答题、多人pk答题、积分兑换、排行榜等七大功能模块页面&#xff0c;适用…

记一次学习--内网穿透

目录 环境搭建 两张网卡如何配置 Ubuntu配置 渗透 ubuntu的拿下 centos的拿下 探测内网环境 fscan扫描 msf上马 渗透 拿下bage cms windows的拿下 ​编辑 使用fscan查看内网环境&#xff0c;发现了192.168.110.128这台设备 使用msf上马&#xff0c;现在这台机器是…

npm安装electron报错 RequestError: connect ETIMEDOUT 185.199.110.133:443

文章目录 npm安装electron报错的问题解决办法 npm安装electron报错的问题 报错信息如下&#xff1a; 由于网络原因一直报错&#xff0c;但是安装其他依赖没问题&#xff0c;查看源&#xff0c;使用淘宝源&#xff0c;也无效 解决办法 设置electron_mirror专用源: npm con…

C++入门基础知识57——【关于C++日期 时间】

成长路上不孤单&#x1f60a;【14后&#xff0c;C爱好者&#xff0c;持续分享所学&#xff0c;如有需要欢迎收藏转发&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#x1f60a;&#xff01;&#xff01;&#xff01;&#xff01;&#xff…

分布式部署①

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 1. 需要部署的服务 Nacos 理论上,应…

Popup源码分析 -- ant-design-vue系列

Popup源码分析 – ant-design-vue系列 1 极简代码 直接返回两个组件&#xff1a;Mask 和 PopupInner&#xff0c;后者在上一篇已经分析过了。下面我们先看一下 Mask的源码。 setup(props, { slots }) {return () > {if (!props.visible) return null;return (<div cla…

【Qt】窗口移动和大小改变事件

窗口移动和大小改变事件 moveEvent窗口移动时触发的事件resizeEvent窗口大小改变时触发的事件 例子&#xff1a;测试移动窗口和改变窗口事件 代码展示 #include "widget.h" #include "ui_widget.h"#include <QDebug> #include <QMoveEvent> …

chapter13-常用类——(String类)——day16

目录 477-StringBuffer方法 477-StringBuffer练习 479-StringBuilder结构剖析 480-StringBuilder应用 477-StringBuffer方法 三个字换两个字 477-StringBuffer练习 1、下面那个StringBuffer&#xff08;str&#xff09;有参构造器&#xff0c;在传入的是null的时候会报错&a…

mybatisplus使用OptimisticLockerInnerInterceptor实现版本号乐观锁

目录 OptimisticLockerInnerInterceptor 介绍 创建项目 创建项目 引入依赖 创建数据表 application.yml配置 项目结构 配置乐观锁拦截器 创建实体类 创建mapper 创建service 创建返回包装类BaseResponse 创建UserController 测试 查询 修改 ​编辑 修改后再查…

imu+wheel融合

ImuWheel融合 文章目录 ImuWheel融合1 轮速计1.1 航迹递推1.1.1 基于欧拉法1.1.2 基于二阶Runge-Kutta积分1.1.3 群空间闭式积分 1.2 雅可比计算 2 IMU观测更新3 数据处理 1 轮速计 1.1 航迹递推 ​ 常见的轮速计积分的方式有三种&#xff1a;欧拉积分、二阶Runge-Kutta积分、…

拯救者y9000p外接显示器黑屏

一开始会出现偶尔黑屏的情况&#xff0c;短则一两秒&#xff0c;长则五分钟。开始以为是屏幕或者是hdmi线的问题。后来网上查&#xff0c;发现可能是联想自带的XRite颜色校准器。 如果不需要该软件可以设置成为开机禁用&#xff0c;这样暂时就没问题了。

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣3217, 82, 237

总结&#xff0c;删除链表节点问题使用到列表&#xff0c;哈希表&#xff0c;递归比较容易超时&#xff0c;我觉得使用计数排序比较稳&#xff0c;处理起来也不是很难。 1. 力扣3217&#xff1a;从链表中移除在数组中的节点 1.1 题目&#xff1a; 给你一个整数数组 nums 和一…

LVM在Kubernetes下的最佳实践方案--TopoLVM

TopoLVM介绍及实践 LVM在Kubernetes下的最佳实践方案–TopoLVM。 1. 简介 TopoLVM 是一种基于 LVM&#xff08;Logical Volume Manager&#xff09;的 CSI&#xff08;Container Storage Interface&#xff09;插件&#xff0c;专为 Kubernetes 环境设计&#xff0c;旨在提供…

分布式部署②

&#x1f4d1;打牌 &#xff1a; da pai ge的个人主页 &#x1f324;️个人专栏 &#xff1a; da pai ge的博客专栏 ☁️宝剑锋从磨砺出&#xff0c;梅花香自苦寒来 对第四台服务器的补充 产品服务,订…

HTML 超链接

每一个网站都是由许多独立的网页组成&#xff0c;网页之家通常都是通过超链接来相互连接的。超链接可以让用户在各个独立的网页之间跳转。 <!DOCTYPE html> <html> <head><meta charset"utf-8" /><title>colspan属性</title>&l…

Linux一周大项目:库的移植

挂载--->将所需库文件夹复制到nfs文件夹中&#xff08;不在终端进行&#xff09;--->cp库文件到开发板 /usr/lib step1 step3 ​​​​​​​​​​​​​​ 一、解压文件 解压zip文件 sudo unzip xxx.zip 解压tar文件 sudo tar -xvf xxx.tar 修改权限 sudo ch…

Maven 依赖漏洞扫描检查插件 dependency-check-maven 的使用

前言 在现代软件开发中&#xff0c;开源库的使用愈加普遍&#xff0c;然而这些开源库中的漏洞往往会成为潜在的安全风险。如何及时的发现依赖的第三方库是否存在漏洞&#xff0c;就变成很重要了。 本文向大家推荐一款可以进行依赖包漏洞检查的 maven 插件 dependency-check-m…

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署GitLab服务器

828华为云征文&#xff5c;华为云Flexus云服务器X实例之openEuler系统下部署Gitlab服务器 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、GitLab介绍2.1 GitLab简介2.2 GitLab主要特点 三、本次…