3---C++之list(逻辑梳理、简单使用演示、部分源码实现)

一、先决知识点1——认识list:

  • list底层实现是双向链表,但是不是循环链表。
  • list是否使用哨兵节点,是细节问题,C++标准并未规定。
  • list是链表,他的优势在于对节点的操作会十分灵活,因此它在需要频繁插入和删除元素的情况下非常高效。
  • list是链表的原因,他的元素分布不再是连续的空间,所以使用‘[ ]’来随机访问会使得性能消耗过大,所以C++标准不支持使用'[ ]'实现访问数据。

二、先决知识2——迭代器的分类: 

  • 根据迭代器的访问能力,可以将迭代器分为三类:单向迭代器、双向迭代器、随机访问迭代器。
  • 单向迭代器:只支持++,例如单链表
  • 双向迭代器:支持++和--,例如双向链表,红黑树
  • 随机访问迭代器:支持++/--/+/- 等,例如vector,string
  • 随机访问迭代器支持所有单向/双向迭代器的功能,因此可以向支持随机访问迭代器作为参数的函数,传递单向/双向迭代器作为参数。反之则不行。

三、简单的使用演示(vector/string中使用方法不变的不再赘述):

3.1排序——sort:

  • list内部实现了sort方法,默认升序。

  • 由于list是链表,他的sort在底层是归并排序而非快排。因此效率并不高,当数据量很大时,归并和快排的效率差距很大。数据量大时,先转换为vector排序后再转化为list可行。

3.2去重——unique:

  • 要求list有序,可以先sort再unique。

3.3删除——remove:

  • 删除所有指定值。

3.4转移——splice:

  • 把一个list的节点摘下来插到另一个list
void splice (iterator position, list& x);
void splice (iterator position, list& x, iterator i);
void splice (iterator position, list& x, iterator first, iterator last);

 四、底层功能实现(第一版,部分功能不是很完善,适合先了解逻辑):

4.1节点类:

  • 节点类,每个链表节点包含三个成员,分别是节点数据、上一个节点地址、下一个节点地址。
  • 创建哨兵节点时,哨兵节点不存储节点数据,所以可以使用缺省值;在C++中,内置类型也有默认构造,T()可以初始化一个类型为T的对象,调用其默认构造函数
template<class T>//模板
struct list_node//节点类
{T _data;list_node<T>* _next;list_node<T>* _prev;//一个指向上一个节点,一个指向下一个节点,一个存储节点值list_node<T>(const T& x = T())//内置类型也有匿名对象:_data(x), _prev(nullptr), _next(nullptr){}
};

4.2迭代器类:

按照需求,实现的逻辑顺序:

1.重命名,简化书写:

		typedef list_node<T> Node;//对节点对象重命名typedef __list_iterator<T> self;//对自己重命名

2.构造函数和成员:

  • _node是一个指针,通过构造函数初始化,指向传过来的节点的地址。
		Node* _node;//创建一个指针__list_iterator(Node* node):_node(node)//指针指向传递的节点对象{}

 3.++/--的运算符重载:

  • list是链表,要实现节点之间的迭代,就需要‘封装+运算符重载’。
  • 后置++/--,加上一个参数int来和前置++/--构成函数重载;
  • 由于后置++/--会创建临时对象,所以资源消耗会大于前置++/--,推荐使用前置代替后置。
  • 后置++/--,返回的是临时对象,所以不能使用引用返回。
		self& operator++()//向后挪动一个节点{_node = _node->_next;return *this;}self& operator--(){_node = _node->_prev;return *this;}self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}

4.*的运算符重载:

  •  返回节点处的数据,如果使用引用返回,还可以修改节点数据。
		T& operator*()//获取节点对象处值,引用返回{return _node->_data;}

5. ==和!=的运算符重载:

  • 比较两个节点的地址是否相同。
		bool operator!=(const self& s)//判断两节点地址是否相等{return _node != s._node;}bool operator==(const self& s)//判断两节点地址是否相等{return _node == s._node;}

 6.完整代码:

	template<class T>struct __list_iterator//迭代器对象{typedef list_node<T> Node;//对节点对象重命名typedef __list_iterator<T> self;//对自己重命名Node* _node;//创建一个指针__list_iterator(Node* node):_node(node)//指针指向传递的节点对象{}self& operator++()//向后挪动一个节点{_node = _node->_next;return *this;}self& operator--(){_node = _node->_prev;return *this;}self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}T& operator*()//获取节点对象处值,引用返回{return _node->_data;}bool operator!=(const self& s)//判断两节点地址是否相等{return _node != s._node;}bool operator==(const self& s)//判断两节点地址是否相等{return _node == s._node;}};

4.3链表类:

按照需求,实现的逻辑顺序:

1.私有成员:

  • _head(哨兵节点)  +  _size(链表长度)
  • 哨兵节点不存储数据。
  • 链表长度‘_size’,在库中实现的list中并没有,加上这个私有成员,主要是方便返回链表的长度,无需再遍历一遍链表来计数链表长度。
	private:Node* _head;//哨兵节点size_t _size;

 2.重命名:

		typedef list_node<T> Node;//重命名节点typedef __list_iterator<T> iterator;//重命名迭代器

3.无参构造函数:

  • list()  +  empty_init()
  • 库中的无参构造调用了一个empty_init()函数初始化哨兵节点。
  • 初始化哨兵节点,将他的next和prev指针都指向自己即可,然后将链表长度初始化为0。
		void empty_init()//无参构造初始化哨兵节点{_head = new Node;//开辟一个空节点_head->_next = _head;//头节点的next和prev都指向自己_head->_prev = _head;_size = 0;}list(){empty_init();}

4. 插入函数:

  • 通过传过来的迭代器,在迭代器前面位置插入一个节点
  • 成功插入节点后,链表长度加一
  • 返回插入节点的迭代器,防止迭代器失效问题
		iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);newnode->_prev = prev;prev->_next = newnode;cur->_prev = newnode;newnode->_next = cur;++_size;return iterator(newnode);}

 5.删除节点:

  • 断开迭代器位置的节点,并将其前后两个节点相互连接。
  • 删除节点后要将链表长度减一。
		iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;delete cur;prev->_next = next;next->_prev = prev;--_size;return iterator(next);}

 6.获取链表首尾迭代器:

  • 首节点,就是哨兵节点的下一个节点。
  • 尾节点,就是哨兵节点。
		iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}

7.头插,尾插:

  •  头插,就是在哨兵节点的后一个节点插入节点,也就是begin()函数得到的迭代器的前一个位置插入节点。
  • 尾插,就是在哨兵节点前插入节点。
  • 复用insert即可。
		void push_back(const T& x)//尾插,重点在于处理好头尾新节点的指针指向{//第一版,在没有insert的情况下实现的版本//Node* tail = _head->_prev;//Node* newnode = new Node(x);//newnode->_data = x;//newnode->_next = _head;//_head->_prev = newnode;//tail->_next = newnode;//newnode->_prev = tail;//第二版,复用insertinsert(end(), x);}void push_front(const T& x){insert(begin(), x);}

8.头删,尾删:

  • 和头插,尾插操作的位置相同。
  • 复用erase函数即可。
		void pop_back(){erase(begin());}void pop_front(){erase(--end());}

9.清空list对象:

  • 清理除了哨兵节点以外的所有节点。
  • 创建一个迭代器指向第一个节点(哨兵节点后一个节点),当这个迭代器不和哨兵节点重合,就持续删除节点。
  • 动态更新迭代器,由于erase会返回被删除节点的下一个节点,所以让迭代器每次都等于erase的返回值即可。
		void clear(){iterator it = begin();while (it != end()){it = erase(it);}}

10.析构函数:

  • 析构函数,是删除所有节点,包括哨兵节点。
  • 复用clear函数后,再删除哨兵节点即可。
		~list(){clear();delete _head;_head = nullptr;}

11.list对象节点数量:

		size_t size(){return _size;}

12.拷贝构造:

  • 先用empty_init函数初始化哨兵节点。
  • 再将源对象的数据一个个插入目标对象即可。
		list(list<T>& l){empty_init();for (auto a : l){push_back(a);}}

13.=的运算符重载:

  • 两个版本,第一个版本先将目标对象的节点全部删除,然后将源对象每个节点的值插入目标对象即可。
  • 第二个版本,实现一个swap函数,swap函数参数创建一个匿名对象,该匿名对象拷贝构造源对象;通过两个swap交换目标对象的哨兵节点和链表大小。交换完成后匿名对象被销毁。
		list<T>& operator=(list<int> l){//if (*this != &l)//{//	clear();//	for (auto a : l)//	{//		push_back(a);//	}//}//return *this;//版本二:调用swap函数swap(l);return *this;}void swap(list<T>& l){std::swap(_head, l._head);std::swap(_size, l._size);}

14.完整代码:

	template<class T>class list//链表类{typedef list_node<T> Node;typedef __list_iterator<T> iterator;public:void empty_init()//无参构造初始化头节点{_head = new Node;//开辟一个空节点_head->_next = _head;//头节点的next和prev都指向自己_head->_prev = _head;_size = 0;}list(){empty_init();}list(list<T>& l){empty_init();for (auto a : l){push_back(a);}}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}list<T>& operator=(list<int> l){//if (*this != &l)//{//	clear();//	for (auto a : l)//	{//		push_back(a);//	}//}//return *this;//版本二:调用swap函数swap(l);return *this;}void swap(list<T>& l){std::swap(_head, l._head);std::swap(_size, l._size);}void push_back(const T& x)//尾插,重点在于处理好头尾新节点的指针指向{//第一版,在没有insert的情况下实现的版本//Node* tail = _head->_prev;//Node* newnode = new Node(x);//newnode->_data = x;//newnode->_next = _head;//_head->_prev = newnode;//tail->_next = newnode;//newnode->_prev = tail;//第二版,复用insertinsert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(begin());}void pop_front(){erase(--end());}iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);newnode->_prev = prev;prev->_next = newnode;cur->_prev = newnode;newnode->_next = cur;++_size;return iterator(newnode);}iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;delete cur;prev->_next = next;next->_prev = prev;--_size;return iterator(next);}size_t size(){return _size;}private:Node* _head;//头节点size_t _size;};

五、const迭代器:

5.1const迭代器和普通迭代器区别:

  •  常规迭代器允许遍历容器并修改容器中的元素。它的使用和指针类似,你可以解引用它来访问和修改元素。
  • const迭代器又名常量迭代器,常量迭代器不允许修改容器中的元素。它只能用于读取元素。这种迭代器用于确保代码的安全性和可读性,防止意外修改元素。
  • 两者的主要区别在于是否允许通过迭代器修改容器中的元素。

5.2const迭代器实现:

  • 要适配const对象和非const对象,就需要写两个版本的迭代器分别对应const对象和非const对象。由于我们写的普通迭代器是一个模板,就需要再写一个const迭代器模板。但是实际上两个模板之间的许多是相同的。
  • 我们可以通过添加模板参数,实现简化代码。
template<class T, class Ref, class Ptr>typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
  • 通过ref和ptr就可以实现通过传过来的参数,实例化具体的模板种类。同一个类模板,会根据传过来的模板参数不同,实例化出不同的类。
  • 就比如以上的两种传模板参数的方式,由于部分模板参数不同,实例化出的就是两个不同的类。

5.3修改*和[]运算符的重载:

  • 由于我们不知道通过模板具体实例化出的是普通迭代器还是const版本的迭代器,所以我们通过模板参数来替代返回类型。
		Ref operator*()//ref传过来的是T&或const T&{return _node->_data;}Ptr operator->()//ptr传过来的是T*或const T*{return &_node->_data;}

5.4添加begin()和end()的const版本:

		const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}

六、打印函数和他的模板:

  •  我们如果要实现打印任意类型的list,就需要使用模板实现print_list()函数。
  • 下面这样写,运行不通过。
	template<typename T>void print_list(const list<T>& l){list<T>::const_iterator it = l.begin();while (it != l.end()){cout << *it << ' ';++it;}cout << endl;}
  • 原因是:在C++中,当你在模板中使用依赖于模板参数的嵌套类型时,例如list<T>::const_iterator,编译器不知道这是一个静态成员或者一个静态函数还是一个类型。

  • 因此需要使用 typename 关键字明确告诉编译器它是一个类型。

	template<typename T>void print_list(const list<T>& l){typename list<T>::const_iterator it = l.begin();while (it != l.end()){cout << *it << ' ';++it;}cout << endl;}
  • 上面是只针对list的打印模板,下面我们升级以下,让这个打印模板可以打印任意类型。
	template<typename Container>void print_container(const Container& con){typename Container::const_iterator it = con.begin();while (it != con.end()){cout << *it << ' ';++it;}cout << endl;}

七、第二版(添加了const迭代器),完整的头文件代码:

测试函数可以写在demo命名空间中,在测试文件的主函数调用,要注意在测试文件包含要调用到的库。

#pragma oncenamespace demo
{template<class T>//模板struct list_node//节点对象{T _data;list_node<T>* _next;list_node<T>* _prev;//一个指向上一个节点,一个指向下一个节点,一个存储节点值list_node<T>(const T& x = T())//内置类型也有匿名对象:_data(x), _prev(nullptr), _next(nullptr){}};template<class T, class Ref, class Ptr>//多加两个模板参数对应&和*的模板//template<class T>struct __list_iterator//迭代器对象{typedef list_node<T> Node;//对节点对象重命名typedef __list_iterator<T, Ref, Ptr> self;//typedef __list_iterator<T> self;//对自己重命名Node* _node;//创建一个指针__list_iterator(Node* node):_node(node)//指针指向传递的节点对象{}self& operator++()//向后挪动一个节点{_node = _node->_next;return *this;}self& operator--(){_node = _node->_prev;return *this;}self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}Ref operator*()//ref传过来的是T&或const T&{return _node->_data;}Ptr operator->()//ptr传过来的是T*或const T*{return &_node->_data;}bool operator!=(const self& s)//判断两节点地址是否相等{return _node != s._node;}bool operator==(const self& s)//判断两节点地址是否相等{return _node == s._node;}};template<class T>class list//链表对象{typedef list_node<T> Node;public:typedef __list_iterator<T, T&, T*> iterator;//普通迭代器的类typedef __list_iterator<T, const T&, const T*> const_iterator;//const迭代器的类//typedef __list_iterator<T> iterator;void empty_init()//无参构造初始化头节点{_head = new Node;//开辟一个空节点_head->_next = _head;//头节点的next和prev都指向自己_head->_prev = _head;_size = 0;}list(){empty_init();}list(const list<T>& l)//需要先实现const迭代器后,才能使用{empty_init();for (auto a : l){push_back(a);}}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}list<T>& operator=(list<int> l){//if (*this != &l)//{//	clear();//	for (auto a : l)//	{//		push_back(a);//	}//}//return *this;//版本二:调用swap函数swap(l);return *this;}void swap(list<T>& l){std::swap(_head, l._head);std::swap(_size, l._size);}void push_back(const T& x)//尾插,重点在于处理好头尾新节点的指针指向{//第一版,在没有insert的情况下实现的版本//Node* tail = _head->_prev;//Node* newnode = new Node(x);//newnode->_data = x;//newnode->_next = _head;//_head->_prev = newnode;//tail->_next = newnode;//newnode->_prev = tail;//第二版,复用insertinsert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(begin());}void pop_front(){erase(--end());}iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator begin() const//const迭代器{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);newnode->_prev = prev;prev->_next = newnode;cur->_prev = newnode;newnode->_next = cur;++_size;return iterator(newnode);}iterator erase(iterator pos){Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;delete cur;prev->_next = next;next->_prev = prev;--_size;return iterator(next);}size_t size(){return _size;}private:Node* _head;//头节点size_t _size;};template<typename Container>void print_container(const Container& con){typename Container::const_iterator it = con.begin();while (it != con.end()){cout << *it << ' ';++it;}cout << endl;}
}

八、list和vector的比较:

  • list使用双向链表实现,节点存储不连续;vector使用动态数组实现,元素在内存中是连续存储的。
  • vector支持随机访问,访问某个元素效率O(1);list不支持随机访问,访问某个元素
    效率O(n)。
  • vector底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 ;list底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低。
  • list不会导致迭代器失效,vector删除、插入数据都会导致迭代器失效。

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

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

相关文章

【UnityShader入门精要学习笔记】第十六章 Unity中的渲染优化技术 (下)

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 减少需要处…

德人合科技——天锐绿盾内网安全管理软件 | -文档透明加密模块

天锐绿盾文档加密功能能够为各种模式的电子文档提供高强度加密保护&#xff0c;丰富的权限控制以及灵活的应用管理&#xff0c;帮助企业构建更严密的立体保密体系。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee ————…

BioVendor—sHLA-G ELISA试剂盒

人类白细胞抗原-G (HLA-G)与其他MHC类基因的不同之处在于它的低多态性和产生七种HLA-G蛋白的选择性剪接&#xff0c;这些蛋白的组织分布局限于正常的胎儿和成人组织&#xff0c;这些组织对先天和后天免疫细胞都具有耐受性。可溶性HLA-G是一种免疫抑制分子&#xff0c;诱导活化的…

详细解析Barlow Twins:自监督学习中的创新方法

首先先简单了解一下机器学习中&#xff0c;主要有三种学习范式&#xff1a;监督学习、无监督学习和自监督学习&#xff1a; 监督学习&#xff1a;依赖带标签的数据&#xff0c;通过输入输出映射关系进行训练。无监督学习&#xff1a;不依赖标签&#xff0c;关注数据的内在结构…

pikachu靶场(unsafe upfileupload(文件上传)通关教程)

目录 client check 1.在桌面新建一个文本文档 2.保存为.png格式 3.打开网站 4.按照图中操作 5.点击forward 6.访问 MIME type 1.新建一个php文件&#xff0c;里面写上 2.上传文件&#xff0c;就是我们保存的文件 3.打开抓包工具&#xff0c;点击开始上传 4.修改Conen…

ADC数模转换器

一、ADC&#xff08;Analog-Digital Converter&#xff09;模拟-数字转换器 1、ADC可以将引脚上连续变化的模拟电压转换为内存中存储的数字变量&#xff0c;建立模拟电路到数字电路的桥梁 2、12位逐次逼近型ADC&#xff0c;1us转换时间 3、输入电压范围&#xff1a;0~3.3V&a…

【K8s】专题四(2):Kubernetes 控制器之 Deployment

以下内容均来自个人笔记并重新梳理&#xff0c;如有错误欢迎指正&#xff01;如果对您有帮助&#xff0c;烦请点赞、关注、转发&#xff01;欢迎扫码关注个人公众号&#xff01; 目录 一、基本介绍 二、工作原理 三、相关特性 四、资源清单&#xff08;示例&#xff09; 五…

【Linux】多线程——线程概念|进程VS线程|线程控制

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;理解【Linux】多线程——线程概念|进程VS线程|线程控制 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! &…

产品上市新闻稿怎么写?纯干货

一个产品的上市&#xff0c;想要达到一个非常好的宣传效果&#xff0c;前期的预热造势是必不可少的&#xff0c;投放产品上市新闻稿到权威专业的媒体&#xff0c;潜移默化去影响用户的心智&#xff0c;产品上市新闻稿怎么写&#xff1f;接下来伯乐网络传媒就来给大家分享一下&a…

重生之 SpringBoot3 入门保姆级学习(10、日志基础与使用)

重生之 SpringBoot3 入门保姆级学习&#xff08;10、日志基础使用&#xff09; 3.1 日志基础3.2 使用日志3.2.1 基础使用3.2.2 调整日志级别3.2.3 带参数的日志 3.1 日志基础 SpringBoot 默认使用 SLF4j&#xff08;Simple Logging Facade for Java&#xff09;和 Logback 实现…

码蹄集部分题目(2024OJ赛17期;二分+差分+ST表+单调队列+单调栈)

1&#x1f40b;&#x1f40b;小码哥处理订单&#xff08;钻石&#xff1b;二分差分&#xff09; 时间限制&#xff1a;1秒 占用内存&#xff1a;128M &#x1f41f;题目描述 &#x1f41f;题目思路 【码蹄集进阶塔全题解07】算法基础&#xff1a;二分 MT2070 – MT2079_哔哩…

Element ui 快速入门(基础知识点)

element ui官网 前言&#xff1a; 在当今时代&#xff0c;我们在编写计算机程序时&#xff0c;不仅仅是写几个增删改查的简单功能&#xff0c;为了满足广大用户对页面美观的需求&#xff0c;为了让程序员们写一些功能更简便&#xff0c;提高团队协作效率&#xff0c;所以eleme…

python操作mongodb底层封装并验证pymongo是否应该关闭连接

一、pymongo简介 github地址&#xff1a;https://github.com/mongodb/mongo-python-driver mymongo安装命令&#xff1a;pip install pymongo4.7.2 mymongo接口文档&#xff1a;PyMongo 4.7.2 Documentation PyMongo发行版包含Python与MongoDB数据库交互的工具。bson包是用…

【Python】解决Python报错:AttributeError: ‘int‘ object has no attribute ‘xxx‘

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

RLC防孤岛保护装置如何工作的?

什么是RLC防孤岛保护装置&#xff1f; 孤岛保护装置是电力系统中一道强大的守护利器&#xff0c;它以敏锐的感知和迅速的反应&#xff0c;守护着电网的平稳运行。当电网遭遇故障或意外脱离主网时&#xff0c;孤岛保护装置如同一位机警的守门人&#xff0c;立刻做出决断&#xf…

Go微服务: 基于Docker搭建Kong网关环境

概述 在当今的微服务架构中&#xff0c;API网关扮演着至关重要的角色&#xff0c;它作为系统的统一入口负责处理所有内外部请求&#xff0c;实现路由转发、负载均衡、安全控制、限流熔断等多种功能Kong&#xff0c;作为一个开源、高性能、可扩展的API网关&#xff0c;凭借其强…

【机器学习】探索未来科技的前沿:人工智能、机器学习与大模型

文章目录 引言一、人工智能&#xff1a;从概念到现实1.1 人工智能的定义1.2 人工智能的发展历史1.3 人工智能的分类1.4 人工智能的应用 二、机器学习&#xff1a;人工智能的核心技术2.1 机器学习的定义2.2 机器学习的分类2.3 机器学习的实现原理2.4 机器学习的应用2.5 机器学习…

在PostGIS中检查孤线(Find isolated lines in PostGIS)

场景 在PostGIS中有一张线要素表,需要检查该表中的孤线,并且进行自动纠正的计算。 其中孤线定义为两端端点都不在任何其他线的顶点上。 本文介绍在PostGIS中的线要素点,通过函数计算指定线要素表中的孤线,并计算最接近的纠偏位置。 In PostGIS, there is a table of line …

GPT-4o(OpenAI最新推出的大模型)

简介&#xff1a;最近&#xff0c;GPT-4o横空出世。对GPT-4o这一人工智能技术进行评价&#xff0c;包括版本间的对比分析、GPT-4o的技术能力以及个人感受等。 方向一&#xff1a;对比分析 GPT-4o&#xff08;OpenAI最新推出的大模型&#xff09;与GPT-4之间的主要区别体现在响应…

268 基于matlab的模拟双滑块连杆机构运动

基于matlab的模拟双滑块连杆机构运动&#xff0c;并绘制运动动画&#xff0c;连杆轨迹可视化输出&#xff0c;并输出杆件质心轨迹、角速度、速度变化曲线。可定义杆长、滑块速度&#xff0c;滑块初始位置等参数。程序已调通&#xff0c;可直接运行。 268 双滑块连杆机构运动 连…