【C++进阶之路】list的基本使用和模拟实现

news2024/11/24 18:35:36

文章目录

  • 初步认识
    • ①定义
    • ②底层原理
    • ③迭代器的分类
  • 一、基本使用
    • 1.插入结点元素
    • 2.删除结点元素
    • 3.合并两个有序链表
    • 4.将一条链表的某一部分转移到另一条链表
    • 5.对链表排序并去重
    • 6.vector与list排序的比较
  • 二、模拟实现
    • ①要点说明
    • ②基本框架
    • ③迭代器
      • 构造函数
      • ++
      • - -
      • *
      • ->
      • list里的迭代器
    • ④insert
    • ⑤erase
    • ⑥push_back
    • ⑦push_front
    • ⑧pop_front
    • ⑨pop_back()
    • ⑩构造函数
    • ⑪clear
    • ⑫析构函数
    • ⑬赋值重载
  • 源码
  • 总结

初步认识

 在正式讲解之前,我们先简单的认识一下list

①定义

template < class T, class Alloc = allocator<T> > class list;

同样这里有两个模板参数,第一个是实例化的类型,第二个是空间配置器

②底层原理

库里的list是采用带头双向循环链表进行实现的。

  • forward_list是单链表

在这里插入图片描述

  • 带哨兵位的头结点,是为了给end()迭代器留位置。

③迭代器的分类

在这里插入图片描述

1.输入迭代器
2.输出迭代器
3.单向迭代器——forward_list,unorder_xxx
4.双向迭代器——list/map/set
5.随机访问迭代器——vector/string/deque

之间的关系:
在这里插入图片描述

  • 从左到右是被包含的关系。

在这里插入图片描述

一、基本使用

1.插入结点元素

	list<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(6);

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
	
	list<int>::iterator it = lt.begin();
	for (size_t i = 0; i < 5; i++)
	{
		it++;
	}
	//在第五个位置的前面插入10
	//第五个位置——是下标为5,也就是第六个元素。
	lt.insert(it,10);


	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

在这里插入图片描述

  • 说明:因为list不支持随机访问,因此没有+,+=,[] 运算符重载,但支持++,- -

  • 这里的迭代器,在插入之后,并没有失效,因为结点的插入是单个申请,单个释放的。不存在 扩容的现象。

2.删除结点元素

void test_list()
{
	list<int> lt;
	lt.push_back(2);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(90);
	lt.push_back(6);
	lt.push_back(7);


	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	//删除所有的偶数结点
	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		if (*it % 2 == 0)
		{
			it = lt.erase(it);
		}
		else
		{
			it++;
		}
	}

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

}
  • erase在释放之后,迭代器是肯定失效的,因为其所指向的空间都失效了。因此库里采用返回值进行更新迭代器。

3.合并两个有序链表

  • 前提必须有序的。
void test_list()
{
	list<int> lt;
	lt.push_back(2);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(6);
	lt.push_back(7);


	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(3);
	lt1.push_back(5);
	lt1.push_back(7);
	lt1.push_back(9);

	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

	//将lt1合并到lt上
	lt.merge(lt1);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

}

在这里插入图片描述

4.将一条链表的某一部分转移到另一条链表

接口:


void splice (iterator position, list& x);

void splice (iterator position, list& x, iterator i);

void splice (iterator position, list& x, iterator first, iterator last);

注意:迭代器/迭代器区间不能重合!

list<int> lt;
	lt.push_back(2);
	lt.push_back(2);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(5);
	lt.push_back(6);
	lt.push_back(7);


	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(3);
	lt1.push_back(5);
	lt1.push_back(7);
	lt1.push_back(9);

	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

	//将lt1转移到到lt的前面
	lt.splice(lt.begin(),lt1);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	//将lt的后半部分转移到lt1上
	list<int>::iterator it = lt.begin();
	for (size_t i = 0; i < 5; i++)
	{
		it++;
	}
	lt1.splice(lt1.begin(), lt ,it, lt.end());

	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;

	//将lt1的第一个结点移到lt的最前面
	lt.splice(lt.begin(), lt1, lt1.begin());

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

5.对链表排序并去重

  • unique去重的前提是有序
	list<int> lt;
	lt.push_back(2);
	lt.push_back(1);
	lt.push_back(3);
	lt.push_back(4);
	lt.push_back(2);
	lt.push_back(6);
	lt.push_back(4);
	
	lt.sort();
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

	lt.unique();

	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;

6.vector与list排序的比较

void test_list2()
{
	list<int> lt;
	vector<int> v;
	//设置随机数起点
	srand((unsigned)time(nullptr));
	size_t datas_num = 1000000;
	for (size_t i = 0; i < datas_num; i++)
	{
		int n = rand();
		lt.push_back(n);
		v.push_back(n);
	}
	int begin1 = clock();
	sort(v.begin(), v.end());
	int end1 = clock();
	cout <<"vector:" << (end1 - begin1) << endl;

	int begin2 = clock();
	lt.sort();
	int end2 = clock();
	cout <<"list:" << (end2 - begin2) << endl;
}

在这里插入图片描述

  • 结果:list的排序有点挫,不过如果数据量比较小,可以使用list提供的排序,数据量比较大的话,那就将list的数据拷贝到vector,进行排序,再拷贝回去。
  • 注意:在debug下进行测试可能会得到不一样的结果,因为vector是使用递归的快排进行实现的,如果debug下添加的调试信息会使栈帧的开销较大,从而影响时间的效率。而list的底层原理是归并,虽然也是递归,但调试信息添加的可能较少,因此会较快。而realease版本是发型版本是做了优化的,性能更好。

二、模拟实现

①要点说明

  • 1.为了不与库里面的list冲突,我们需要命名空间对自己实现的类进行封装
  • 2.这里我们实现的框架是按照带头双向循环链表的数据结构进行实现的。
  • 3.为了理解,下面的接口是分开讲解的,最后我会给出源码

②基本框架

template<class  T>
struct list_node
{
	list_node(const T& val = T())
		:_val(val)
		,_next(nullptr)
		,_prev(nullptr)
	{}

	T _val;
	list_node* _next;
	list_node* _prev;
};

template<class T>
class list
{
	typedef list_node<T> Node;
public:
	
private:
	Node* _head;
	size_t _size;
};

③迭代器

  • 这是list的最关键的部分!

我们先来谈谈是如何实现的,迭代器必然是指向一个结点的指针,但是能完成++操作,以及解引用操作,那该怎么办呢?答案其实很简单——用类进行封装,通过运算符重载实现相应的功能

简单的迭代器框架:

template<class T>
struct list_iterator
{
	typedef list_iterator<T> iterator;	
	Node* _node;
};

构造函数

list_iterator(Node* val = nullptr)
	:_node(val)
{}
list_iterator(const iterator& lt)
	:_node(lt._node)
{}
  • 析构我们使用默认生成的即可,可千万不要写析构释放迭代器指向的空间,因为迭代器指向的空间属于list。应由list进行管理。

剩下的操作是,类里面进行重载++,-- ,*。

++

//前置
iterator& operator ++() 
{
	_node = _node->_next;
	return *this;
}
//后置
iterator operator ++(int)
{
	list_iterator tmp(_node);
	_node = _node->_next;
	return tmp;
}

- -

//前置
iterator& operator --()
{
	_node = _node->_prev;
	return *this;
}
//后置
iterator operator --(int)
{
	list_iterator tmp(_node);
	_node = _node->_prev;
	return tmp;
}

*

T& operator *()
{
	return _node->_val;
}

这样迭代器的基本功能就实现了,但是const的迭代器该如何实现呢?

是这样么?

typedef const list_iterator<T> const_iterator;

这样迭代器的成员变量不可被修改,即不可以++或者- -,不符合要求,我们想要的只是返回的值不可被修改

一般都会再拷贝一份进行,将拷贝的迭代器改改,但是我们还可以设置第二个模板参数,这样外面可以这样定义。

template<class Tclass Ref> struct list_iterator;
//里面的运算符重载——*
Ref operator *()
{
	return _node->_val;
}

//list里面可以这样定义

typedef list_iterator<T,const T&> const_iterator;
typedef list_iterator<T,T&> iterator;
  • 这样问题就解决了。

->

还有第二个问题,如果迭代器指向的是自定义类型,比如:

struct A
{
	A(int x = 0)
		:_a1(x)
	{}
	int _a1;
};

如果想要访问其成员,直接解引用是不行的。

我们可以这样:

iterator it = lt.begin();
(*it)._a1;

访问其元素。

我们还可以重载-> 进行访问:

T* operator ->()
{
	return &(_node->_val);
}
iterator it = lt.begin();
it->_a1;

这样其实省略了一个箭头,这省略的一个箭头其实是为了美观,编译器在处理时,会自动加上的。

如果是const对象呢?其实处理方式一样的,再加一个模板参数。

更新后的模板,和迭代器:

template<class T,class Ref,class Ptr> struct list_iterator;
Ptr operator ->()
{
	return &(_node->_val);
}
typedef list_iterator<T,const T&,const T*> const_iterator;
typedef list_iterator<T,T&,T*> iterator;

那完整版的迭代器即为:

template<class T,class Ref,class Ptr>
struct list_iterator
{
	typedef list_node<T> Node;
	typedef list_iterator<T, Ref, Ptr> self;
	typedef list_iterator<T, T&, T*> iterator;


	list_iterator(Node* val = nullptr)
		:_node(val)
	{}
	list_iterator(const iterator& lt)
		:_node(lt._node)
	{}


	//重载++
	self& operator ++() 
	{
		_node = _node->_next;
		return *this;
	}
	//后置++,为了区别需要重载一下,这里的参数实际上没啥用
	self operator ++(int)
	{
		list_iterator tmp(_node);
		_node = _node->_next;
		return tmp;
	}
	self& operator --()
	{
		_node = _node->_prev;
		return *this;
	}
	self operator --(int)
	{
		list_iterator tmp(_node);
		_node = _node->_prev;
		return tmp;
	}
	bool operator != (const self& it) const
	{
		return _node != it._node;
	}
	bool operator == (const self& it) const//const和非const都能用
	{
		return _node == it._node;
	}		
	Ptr operator ->()
	{
		return &(_node->_val);
	}
	Ref operator *()
	{
		return _node->_val;
	}
	Node* _node;
};

list里的迭代器

iterator begin() 
{
	return _head->_next; 
	//隐式类型转换
}
const_iterator begin()const
{
	return _head->_next;
	
}
iterator end()
{
	return _head;
}
const_iterator end()const
{
	return _head;
}

④insert

//在pos之前插入
void insert(iterator pos, const T& val)
{
	Node* newnode = new Node(val);
	Node* cur = pos._node;
	Node* prev = cur->_prev;

	newnode->_next = cur;
	newnode->_prev = prev;

	cur->_prev = newnode;
	prev->_next = newnode;


	_size++;
}

⑤erase

Node* erase(iterator pos)
{
	assert(pos != end());
	//这个检查是不很严格的,如果删除一个未知的结点,这个是检查不到的!
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* next = cur->_next;

	prev->_next = next;
	next->_prev = prev;

	delete cur;

	_size--;
	return next;
}

⑥push_back

void push_back(const T& val)
{
	//Node* tail = _head->_prev;
	//Node* newnode = new Node(val);

	//newnode->_next = _head;
	//newnode->_prev = tail;

	//_head->_prev = newnode;
	//tail->_next = newnode;
	insert(end(), val);
}

⑦push_front

void push_front(const T& val)
{
	insert(begin(), val);
}

⑧pop_front

void pop_front()
{
	erase(begin());
}

⑨pop_back()

void pop_back()
{
	erase(--end());
}

⑩构造函数

//默认构造函数
list()
{
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
	_size = 0;
}

//拷贝构造
list(const list<T>& lt)
{
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
	_size = 0;

	for (auto e : lt)
	{
		push_back(e);
	}
}

⑪clear

void clear()
{
	iterator it = begin();
	while (it != end())
	{
		it = erase(it);
	}
}

⑫析构函数

~list()
{
	clear();
	delete _head;
}

⑬赋值重载

void swap(list &tmp)
{
	std::swap(_head, tmp._head);
	std::swap(_size, tmp._size);
}
list& operator = (list tmp)
{
	swap(tmp);
	return *this;
}

剩余的接口,过于简单,这里就不再列出了,有兴趣请看源码。

源码

namespace my_list
{
	template<class  T>
	struct list_node
	{
		list_node(const T& val = T())
			:_val(val)
			,_next(nullptr)
			,_prev(nullptr)
		{}

		T _val;
		list_node* _next;
		list_node* _prev;
	};



	template<class T,class Ref,class Ptr>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator<T, Ref, Ptr> self;
		typedef list_iterator<T, T&, T*> iterator;


		list_iterator(Node* val = nullptr)
			:_node(val)
		{}
		list_iterator(const iterator& lt)
			:_node(lt._node)
		{}


		//重载++
		self& operator ++() 
		{
			_node = _node->_next;
			return *this;
		}
		//后置++,为了区别需要重载一下,这里的参数实际上没啥用
		self operator ++(int)
		{
			list_iterator tmp(_node);
			_node = _node->_next;
			return tmp;
		}
		self& operator --()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator --(int)
		{
			list_iterator tmp(_node);
			_node = _node->_prev;
			return tmp;
		}
		bool operator != (const self& it) const
		{
			return _node != it._node;
		}
		bool operator == (const self& it) const//const和非const都能用
		{
			return _node == it._node;
		}		
		Ptr operator ->()
		{
			return &(_node->_val);
		}
		Ref operator *()
		{
			return _node->_val;
		}
		Node* _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;

		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}
		list(const list<T>& lt)
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;

			for (auto e : lt)
			{
				push_back(e);
			}
		}
		~list()
		{
			clear();
			delete _head;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
			//cout << _size << endl;
		}
		iterator begin() 
		{
			//return _head->_next;
			//隐式类型转换为list_itertor
			
			return _head->_next; 
		}
		const_iterator begin()const
		{
			//return _head->_next;
			//隐式类型转换为list_itertor

			return _head->_next;
		}
		iterator end()
		{
			return _head;
			//返回的是_head的拷贝,因此返回对象具有常属性
			
			//隐式类型转换为list_itertor
			//return itrator(_head->next); 
			//匿名对象
		}
		const_iterator end()const
		{
			return _head;
			//返回的是_head的拷贝,因此返回对象具有常属性

			//隐式类型转换为list_itertor
			//return itrator(_head->next); 
			//匿名对象
		}
		void push_back(const T& val)
		{
			//Node* tail = _head->_prev;
			//Node* newnode = new Node(val);

			//newnode->_next = _head;
			//newnode->_prev = tail;

			//_head->_prev = newnode;
			//tail->_next = newnode;
			insert(end(), val);
		}
		//在pos之前插入
		void insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val);
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			newnode->_next = cur;
			newnode->_prev = prev;

			cur->_prev = newnode;
			prev->_next = newnode;


			_size++;
		}
		Node* erase(iterator pos)
		{
			assert(pos != end());
			//这个检查是不很严格的,如果删除一个未知的结点这个是会报错的!
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;

			prev->_next = next;
			next->_prev = prev;

			delete cur;

			_size--;
			return next;
		}
		void pop_back()
		{
			erase(--end());
		}
		void pop_front()
		{
			erase(begin());
		}
		void push_front(const T& val)
		{
			insert(begin(), val);
		}
		size_t size()
		{
			return _size;
		}
		bool empty()
		{
			return _size == 0;
		}
		void swap(list &tmp)
		{
			std::swap(_head, tmp._head);
			std::swap(_size, tmp._size);
		}
		list& operator = (list tmp)
		{
			swap(tmp);
			return *this;
		}

	private:
		Node* _head;
		size_t _size;
	};
}

总结

 今天的分享就到这里了,如果觉得文章不错,点个赞鼓励一下吧!我们下篇文章再见

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

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

相关文章

HG20202-2014脱脂工程施工及验收规范

为提高脱脂工程施工技术水平,加强施工过程的质量控制,保证施工质量和安全,制定本规范。 本规范适用于化工建设工程中忌油工艺介质系统的设备、管道和管道组成件仪表和仪表组成件等的脱脂。 本规范不适用于下列情况的脱脂: 1、制造领域; 2、工厂停车检修。 设计文件或用户规…

2023云曦期末复现

目录 WEB sign SSTI serialize WEB sign 有10000个 进行bp爆破 能发现 410 和 414长度 还有 420 410 414存在16进制的字符 拼凑出来为 \x66\x6c\x61\x67\x7b\x61\x63\x63\x39\x39\x66\x39\x30\x34\x66\x30\x65\x61\x66\x61\x34\x31\x63\x30\x36\x34\x33\x36\x38\x31\x3…

行为型模式 - 策略模式

概述 先看下面的图片&#xff0c;我们去旅游选择出行模式有很多种&#xff0c;可以骑自行车、可以坐汽车、可以坐火车、可以坐飞机。 作为一个程序猿&#xff0c;开发需要选择一款开发工具&#xff0c;当然可以进行代码开发的工具有很多&#xff0c;可以选择Idea进行开发&…

WPF嵌入外部exe应用程序-使用Winfom控件承载外部程序

使用Winform控件承载外部程序 在WPF中使用Winfom控件添加winform相关的程序集在XAML头中加入对这两个程序集命名空间的引用使用Winform控件效果&#xff1a;问题 在Winfom控件中嵌入exe程序准备Winfrom控件更换父窗体的句柄完整实现代码&#xff1a;实现效果&#xff1a; 问题和…

王道计算机网络学习笔记(5)——传输层和应用层

前言 文章中的内容来自B站王道考研计算机网络课程&#xff0c;想要完整学习的可以到B站官方看完整版。 五&#xff1a;传输层 5.1&#xff1a;传输层基本概述 传输层的功能&#xff1a; 1传输层提供进程和进程之间的逻辑通信 2复用和分用 微信和QQ都使用传输层的协议进行发…

设计模式-外观模式在Java中的使用示例

场景 外观模式 外观模式是一种使用频率非常高的结构型设计模式&#xff0c;它通过引入一个外观角色来简化客户端与子系统 之间的交互&#xff0c;为复杂的子系统调用提供一个统一的入口&#xff0c;降低子系统与客户端的耦合度&#xff0c;且客户端调用非常方便。 示例 自…

【区块链+体育】“数智化”的杭州亚运会,中创助力区块链技术发展

“智能”&#xff0c;是杭州亚运会的办赛理念之一。除了数字藏品开亚运先河&#xff0c;杭州亚组委充分应用区块链、大数据、人工智能等前沿技术&#xff0c;为观众提供从购票、出行、观赛到住宿、美食和旅游等“一站式”服务。 本次亚运会将全程智能陆续落到了实处&#xff0…

10亿级用户,如何做 熔断降级架构?微信和hystrix的架构对比

说在前面 在40岁老架构师 尼恩的读者社区(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如极兔、有赞、希音、百度、网易、滴滴的面试资格&#xff0c;遇到一几个很重要的面试题&#xff1a; (1) 什么是熔断&#xff0c;降级&#xff1f;如何实现&#xff1f; (2) 服务熔…

测试用例(2)

项目管理工具 主要用tapd&#xff0c;jira少用 acp 敏捷项目管理证书 task:故事&#xff0c;一个故事有开始也有结束&#xff0c;那么在项目管理里面&#xff0c;会把每个任务按照一个task来看&#xff0c;那么这个task也可以叫story&#xff0c;具体指的就是任务有开始有结…

利用鸿鹄优化共享储能的SCADA 系统功能,赋能用户数据自助分析

摘要 本文主要介绍了共享储能的 SCADA 系统大数据架构&#xff0c;以及如何利用鸿鹄来更好的优化 SCADA 系统功能&#xff0c;如何为用户进行数据自助分析赋能。 1、共享储能介绍 说到共享储能&#xff0c;可能不少朋友比较陌生&#xff0c;下面我们简单介绍一下共享储能的价值…

数组的递归筛选

数组递归筛选 根据一个值筛选出来通过 includes 递归 const options [{name: "ikun",options: [{name: "YAY11",},],},{name: "YAY",}, ];function findValue(orgOptions,val) {let newArr1 []orgOptions.forEach(item>{if(item.options…

费尔法克斯水务通过使用 Liquid UI 移动化和定制 SAP PM 来提高收入和数据完整性

背景 费尔法克斯水务是北弗吉尼亚州地区领先的水县。它是华盛顿特区大都会区的三大供水商之一。它每天为近171万居民提供2.<>亿加仑的水。它渴望坚持其愿景&#xff0c;即保持以客户为中心&#xff0c;同时帮助维持该地区的高质量生活和经济状况。 挑战 由于桌面系统&…

Druid-排查conditionDoubleConstAllow配置问题(double const condition)

Druid-排查conditionDoubleConstAllow配置问题(double const condition) 报错信息 Caused by: java.sql.SQLException: sql injection violation, dbType postgresql, druid-version 1.2.18, double const condition : SELECT * FROM test where 11 AND TRUE AND TRUE关键词&…

02-线性结构2 一元多项式的乘法与加法运算

一个小时敲&#xff0c;五分钟改错。比一年前进步还是很大的。 但是如果测试点没有提示的话&#xff0c;改到哪年就不一定了( ◔︎ ‸◔︎) 思路 多项式加法&#xff0c;极其类似Merge &#xff08;测试点2&#xff1a;系数加完要是0的话就不用添入结果多项式里了~&#xff…

业务安全分析第19期 | 今年暑假,博物馆的门票为什么抢不到?

目录 “黄牛”&#xff1a;加价代预约、加价售票、兜售野导游套餐 “黄牛”倒票带来的危害 “黄牛”为什么能够抢到票 博物馆与“黄牛”的门票攻防 “黄牛”使用的作弊软件有什么特征 技术上防范“黄牛”的作弊软件抢票 遏制“黄牛”倒票给博物馆带来的收益 随着暑期参观…

电影《碟中谍7:致命清算(上)》观后感

上周看了电影《碟中谍7&#xff1a;致命清算&#xff08;上&#xff09;》&#xff0c;从电影名称就知道&#xff0c;这部电影会有下部&#xff0c;讲述科学进步之后&#xff0c;有AI引发的技术变革&#xff0c;出现了一种AI变体叫做智体的东西&#xff0c;它有自主意思&#x…

【代码随想录 | Leetcode | 第八天】哈希表 | 有效的字母异位词 | 两个数组的交集 | 两数之和

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏&#xff0c;今天将为大家带来哈希法~有效的字母异位词 | 两个数组的交集 | 两数之和的分享✨ 目录 前言242. 有效的字母异位词349. 两个数组的交集1. 两数之和总结 242. 有效的字母异位词 ✨题目链接点这里 给定两个字符串…

【Vue/element】 el-table实现表格动态新增/插入/删除 表格行,可编辑单元格

el-table实现表格动态新增/插入/删除 表格行&#xff0c;可编辑单元格 效果如下&#xff1a; 点击“新增一行”可以在表格最后新增一行&#xff0c;单元格内容可编辑 点击绿色按钮&#xff0c;可在指定行的后面插入一行 点击红色-按钮&#xff0c;可以删除指定行 原理&#…

【Spring 】执行流程解析:了解Bean的作用域及生命周期

哈喽&#xff0c;哈喽&#xff0c;大家好~ 我是你们的老朋友&#xff1a;保护小周ღ 今天给大家带来的是 Spring 项目的执行流程解析 和 Bean 对象的6 种作用域以及生命周期&#xff0c;本文将为大家讲解&#xff0c;一起来看看叭~ 本期收录于博主的专栏&#xff1a;JavaEE_保…

那些你必须知道的4个matlab小技巧(附最新安装包)

文末福利&#xff1a;MATLAB R2022b软件安装包 MATLAB 简介 01 一、MATLAB简介 数学类科技应用软件包括数值计算&#xff08;Number Crunching&#xff09;型软件和数学分析&#xff08;Math Analysis&#xff09;型软件 数值计算型软件 它们对大批数据具有较强的管理、计…