list(二) (list模拟实现)

news2024/9/20 23:25:25

首先进行大框架  先写基本的结点类  有data next  prev

	template<class T>
	class ListNode//或者使用struct 就不用在写public声明公有
	{
	public:
	//这里不仅仅是成员函数 成员变量也要公有化 
		ListNode<T>*  _next;
		ListNode<T>*  _prev;
	    T _data;
    }

之后是链表list类的构造

template<class T>
class list
{
 typedef ListNode<T> Node;
public:
   list()
   {
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
   }
private:
	Node* _head;
};

私有成员是节点类型的指正 会在构造时指向我们的头节点

这里 我们创建的列表是带头双向循环链表  所以在初始化时让头节点的next 和prev指向自己  

接下来要模拟尾差 push_back  带头双向循环链表  是创建一个新节点  然后让新节点的prev指向尾节点  尾结点的next指向新节点 新节点的next指向头节点 头节点的prev指向新节点  

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

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

这里由于是双向带头循环链表 所以我们的尾节点就是头节点的prev指向的结点 所以我们不需要手动更新尾节点的位置  每次插入完成后 新节点会自动成为尾节点

这里在测试时会出现一些问题

1.按需实例化  那就是一些类和函数的一些语法问题 在没有被调用时系统不会检查报错 只有使用时实例化模版 这是才会报错

2.在上面的节点类中我们只写了成员函数而没有写构造函数 需要补充

3.关于私有公有的问题  我们的类在默认情况下是私有的  只有public下才能允许外界访问  而struct在默认情况下是全部共有的

template<class T>
class ListNode//或者使用struct 就不用在写public声明公有
{
public:
//这里不仅仅是成员函数 成员变量也要公有化 
	ListNode<T>*  _next;
	ListNode<T>*  _prev;
	T _data;

	ListNode(const T& data )//这里也可以用匿名对象写成全缺省
		: _next(nullptr)
		, _prev(nullptr)
		, _data(data) 
	{}
	
};	

这里我们上面的list在构造时并没有在头结点进行初始化赋值 而我们的push_bcak函数对新节点创立时进行了赋值  所以为了保持一致  要对头结点创建时进行赋值 也就是要有一个缺省值  但是这里不能给0  因为date 是T类型的  有可能是double 有可能是string  不能使用0赋值

那我们应该如何赋值给头结点呢 可以考虑给匿名对象

也可以在结点创建处参数写成全缺省

template<class T>
class ListNode//或者使用struct 就不用在写public声明公有
{
public:
//这里不仅仅是成员函数 成员变量也要公有化 
	ListNode<T>*  _next;
	ListNode<T>*  _prev;
	T _data;

	ListNode(const T& data = T() )//这里也可以用匿名对象写成全缺省
		: _next(nullptr)
		, _prev(nullptr)
		, _data(data) 
	{}
	
};	

这时进行测试

void list1()//在没有没有实例化以前检查是没有意义的 因为细节不会检查模版 只有实例化以后才会被检查
{//按需实例化 在没有实例化之前不会检查细节 同时只有调用这部分才会检查这部分(除了一些大的语法错误)在没实例化之前不会检查其对应部分
	list<int> l1;//不调用 就不实例化这个成员函数
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(1);
	l1.push_back(5);
	l1.push_back(10);
	l1.push_back(15);
}

这时push_back就可以正常使用了

接下来要模拟list迭代器  

迭代器++要到达下一个位置 但是这里的list节点不符合  list节点可以通过next指向到达下一个结点  

所以可以通过重载++实现迭代器模拟

template<class T>
class ListIterator//这里也存在私有的问题 需要使用public
{
public://这里也不用太担心公有化访问的 问题 不同的编译器下底层实现的代码是不同的 也就是名字不一定相同 同时正常一般是直接回调用迭代器 不会直接访问类成员
	typedef ListNode<T> Node;
	typedef ListIterator<T> Self;
	Node* _node;

	ListIterator(Node* node)
		:_node(node)
	{}
	//这个迭代器要不要写析构 不要  这个迭代器指向的空间是链表的 不希望被析构
	//这里也不需要拷贝构造 因为这里需要的就是浅拷贝 所以默认生成的拷贝构造就够用  所以默认生成的浅拷贝也是有些意义的 并不是完全没意义
	T& operator * () //每个类都能重载运算符 当重载号运算符之后就能控制一个类的行为
	{
		return _node->_data;
	}
	
	Self& operator ++()
	{
		_node = _node->_next;
		return *this;
	}
bool operator !=(const Self& it)
{
	return _node != it._node;
} 
};

这里迭代器的模拟重新使用了一个类来进行重载  这里的类成员就是一个结点指针  构造是对结点指针进行初始化构造   在一般的迭代器循环中 最常用到的是++  解引用   !=   三个重载   这里先谢了这三个   这里的所有成员变量和函数 都是需要的  所以设置为公有public

实现这个类之后  在list类中typedef  iterator   这里为什么不直接使用listIterator  这是为了统一名称  

使代码使用更加流畅   同时在list类中实现  begin()  end() 成员函数

template<class T>
class list
{
 typedef ListNode<T> Node;

public:
typedef ListIterator<T > iterator; //每个类都要使用iterator去规范迭代器  这样每个容器使用的都是iterator 这样使用起来方便 成本较低


iterator begin()
{
	/*iterator it(_head->_next);
	return it;*/ //定义有名对象不如定义匿名对象
	return iterator(_head -> _next);
}
iterator end()
{
  return iterator (_head);
}

   list()
   {
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;
   }

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

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

private:
	Node* _head;
};

这里对于内置类型无法重载运算符 只有自定义类型才能重载运算符

	void list1()//在没有没有实例化以前检查是没有意义的 因为细节不会检查模版 只有实例化以后才会被检查
	{//按需实例化 在没有实例化之前不会检查细节 同时只有调用这部分才会检查这部分(除了一些大的语法错误)在没实例化之前不会检查其对应部分
		list<int> l1;//不调用 就不实例化这个成员函数
		l1.push_back(1);
		l1.push_back(2);
		l1.push_back(1);
		l1.push_back(5);
		l1.push_back(10);
		l1.push_back(15);

		list<int>::iterator it = l1.begin();
		while (it != l1.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}

这里可以看到我们的初代迭代器是可以正常运行的

接下来我们继续通过重载的方式对迭代器进行进一步的完善  

template<class T>
class ListIterator//这里也存在私有的问题 需要使用public
{
public://这里也不用太担心公有化访问的 问题 不同的编译器下底层实现的代码是不同的 也就是名字不一定相同 同时正常一般是直接回调用迭代器 不会直接访问类成员
	typedef ListNode<T> Node;
	typedef ListIterator<T> Self;
	Node* _node;

	ListIterator(Node* node)
		:_node(node)
	{}
	//这个迭代器要不要写析构 不要  这个迭代器指向的空间是链表的 不希望被析构
	//这里也不需要拷贝构造 因为这里需要的就是浅拷贝 所以默认生成的拷贝构造就够用  所以默认生成的浅拷贝也是有些意义的 并不是完全没意义
	T& operator * () //每个类都能重载运算符 当重载号运算符之后就能控制一个类的行为
	{
		return _node->_data;
	}
	
	Self& operator ++()
	{
		_node = _node->_next;
		return *this;
	}
     Self& operator --()
{
	_node = _node->_prev;
	return *this;
}

    Self  operator ++(int)//这里是后置加加 参数中有int用来与前置++作区分
{
	Self tmp(*this);//这里返回的是加加之前的值的 所以需要提前保存一下
	_node = _node->_next;
	return *tmp;
}

Self  operator --(int)
{
	Self tmp(*this);
	_node = _node->_prev;
	return *tmp;
}


bool operator !=(const Self& it)
{
	return _node != it._node;
} 
  bool operator ==(const Self& it)
{
	return _node == it._node;
}

};

这里注意前置 与后置的 区别 前置返回的是引用的self  而后置则不引用直接返回self  同时为了区分前置和后置 在后置函数的括号中加了int   同时++是向下一个next移动  而--则是像下一个prev移动

这里要不要重载+和-  是不要的  因为+ 和-的效率非常低  如果要加10 那么就要向前走十次  

即便是在std库中也是没有重载+和-的

这里迭代器类是不需要析构函数的  这里并不希望迭代器释放   这里也不要写深拷贝构造 这里默认拷贝构造 和默认析构就够用

这里也支持修改 支持迭代器也就支持范围for

void list1()//在没有没有实例化以前检查是没有意义的 因为细节不会检查模版 只有实例化以后才会被检查
{//按需实例化 在没有实例化之前不会检查细节 同时只有调用这部分才会检查这部分(除了一些大的语法错误)在没实例化之前不会检查其对应部分
	list<int> l1;//不调用 就不实例化这个成员函数
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(1);
	l1.push_back(5);
	l1.push_back(10);
	l1.push_back(15);

	list<int>::iterator it = l1.begin();
	while (it != l1.end())
	{
		*it += 10;
		cout << *it << " ";
		++it;
	}
	cout << endl;

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

这里对迭代器进行进一步的加工

如果存在自定义类型pos类的链表来进行遍历

struct pos//这里需要提供默认构造
{
	int _row;
	int _col;
	pos(int row = 0, int col= 0)//这里写成全缺省提供默认构造
		:_row(row)
		, _col(col)
	{}
};

这是pos类的结构

可见一个pos中分别有横纵坐标

void list2()
{
	list<pos> l1;
	l1.push_back(pos(100,100));
	l1.push_back(pos(200, 200));
	l1.push_back(pos(300, 100));
    list<pos>::iterator it = l1.begin();
    while(it != l1.end())
    {
     cout << *it << " ";
      ++it;
    }
    cout << endl;
}

这时我们的代码是无法正常运行的

正常运行的方法一  通过.来获取成员变量

void list2()
{
	list<pos> l1;
	l1.push_back(pos(100,100));
	l1.push_back(pos(200, 200));
	l1.push_back(pos(300, 100));
    list<pos>::iterator it = l1.begin();
    while(it != l1.end())
    {
     cout << (*it)._row << ":" << (*it). _col << endl;
      ++it;
    }
    cout << endl;
}

方法2 也可以使用箭头->

void list2()
{
	list<pos> l1;
	l1.push_back(pos(100,100));
	l1.push_back(pos(200, 200));
	l1.push_back(pos(300, 100));
    list<pos>::iterator it = l1.begin();
    while(it != l1.end())
    {
     cout << it->_row << ":" << it-> _col << endl;
      ++it;
    }
    cout << endl;
}

但是想要使用箭头 我们就需要再迭代器的类中重载->

	template<class T>
	class ListIterator//这里也存在私有的问题 需要使用public
	{
	public://这里也不用太担心公有化访问的 问题 不同的编译器下底层实现的代码是不同的 也就是名字不一定相同 同时正常一般是直接回调用迭代器 不会直接访问类成员
		typedef ListNode<T> Node;
		typedef ListIterator<T> Self;
		Node* _node;

		ListIterator(Node* node)
			:_node(node)
		{}
		//这个迭代器要不要写析构 不要  这个迭代器指向的空间是链表的 不希望被析构
		//这里也不需要拷贝构造 因为这里需要的就是浅拷贝 所以默认生成的拷贝构造就够用  所以默认生成的浅拷贝也是有些意义的 并不是完全没意义
		T& operator * () //每个类都能重载运算符 当重载号运算符之后就能控制一个类的行为
		{
			return _node->_data;
		}

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

		Self& operator ++()
		{
			_node = _node->_next;
			return *this;
		}
		Self& operator --()
		{
			_node = _node->_prev;
			return *this;
		}

		Self  operator ++(int)//这里是后置加加 参数中有int用来与前置++作区分
		{
			Self tmp(*this);//这里返回的是加加之前的值的 所以需要提前保存一下
			_node = _node->_next;
			return *tmp;
		}

		Self  operator --(int)
		{
			Self tmp(*this);
			_node = _node->_prev;
			return *tmp;
		}


		bool operator !=(const Self& it)
		{
			return _node != it._node;
		}
		bool operator ==(const Self& it)
		{
			return _node == it._node;
		}

	};

这时代码就可以自由使用

这里访问的其实有两箭头   :cout<< it .operator-> ()-> _row <<" :"<< it.operator->() ->_col;

第一个箭头返回 pos*  第二个箭头访问成员变量

但是为了美观 就省略为了一个箭头

const迭代器问题

这里先写一个func函数用来使用const迭代器进行遍历

void func(const list<int>&it)
{ 
	
	 list<int>::iterator it1 = it.begin();
	while (it1 != it.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
}

这里普通迭代器显然是无法使用 需要const迭代器  不能直接使用const修饰iterator  因为const迭代器类似于const指针  const迭代器的目的是想要迭代器指向的内容不能修改 而迭代器本身是可以修改的   直接使用const无法达到效果   

而想要迭代器指向的内容无法修改的核心是通过控制operator * 和 operator->返回的内容不能修改

而这里想要修改 就需要一个新的类 它与普通的迭代器类大体相似 但在一些关键地方上不同

 

template<class T>
class constListIterator//这里也存在私有的问题 需要使用public
{
public://这里也不用太担心公有化访问的 问题 不同的编译器下底层实现的代码是不同的 也就是名字不一定相同 同时正常一般是直接回调用迭代器 不会直接访问类成员
	typedef ListNode<T> Node;
	typedef constListIterator<T> Self;
	Node* _node;

	constListIterator(Node* node)
		:_node(node)
	{}
	//这个迭代器要不要写析构 不要  这个迭代器指向的空间是链表的 不希望被析构
	//这里也不需要拷贝构造 因为这里需要的就是浅拷贝 所以默认生成的拷贝构造就够用  所以默认生成的浅拷贝也是有些意义的 并不是完全没意义
	const T& operator * () //每个类都能重载运算符 当重载号运算符之后就能控制一个类的行为
	{
		return _node->_data;
	}

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

	Self& operator ++()
	{
		_node = _node->_next;
		return *this;
	}
	Self& operator --()
	{
		_node = _node->_prev;
		return *this;
	}

	Self  operator ++(int)//这里是后置加加 参数中有int用来与前置++作区分
	{
		Self tmp(*this);//这里返回的是加加之前的值的 所以需要提前保存一下
		_node = _node->_next;
		return *tmp;
	}

	Self  operator --(int)
	{
		Self tmp(*this);
		_node = _node->_prev;
		return *tmp;
	}


	bool operator !=(const Self& it)
	{
		return _node != it._node;
	}
	bool operator ==(const Self& it)
	{
		return _node == it._node;
	}

};

这里我们只修改类的名字为constiterator  同时对operator* 和operator->返回进行const修饰

同时在list类中 也要加入相对应的const_iterator别名  同时添加const迭代器使用begin和end成员函数

template<class T>
class list
{
	typedef ListNode<T> Node;

public:
	typedef ListIterator<T > iterator; //每个类都要使用iterator去规范迭代器  这样每个容器使用的都是iterator 这样使用起来方便 成本较低
	typedef constListIterator<T> const_iterator;

	iterator begin()
	{
		/*iterator it(_head->_next);
		return it;*/ //定义有名对象不如定义匿名对象
		return iterator(_head->_next);
	}
	iterator end()
	{
		return iterator(_head);
	}

	const_iterator begin() const
	{
		/*iterator it(_head->_next);
		return it;*/ //定义有名对象不如定义匿名对象
		return const_iterator(_head->_next);
	}
	const_iterator end()  const
	{
		return const_iterator(_head);
	}

	list()
	{
		_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;
	}

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

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

	}

private:
	Node* _head;
};

这里进行测试

void func(const list<int>& it)
{

	list<int>::const_iterator it1 = it.begin();
	while (it1 != it.end())
	{
		cout << *it1 << " ";
		++it1;
	}
	cout << endl;
}

void list3()
{
	list<int> l1;
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(1);
	func(l1);
	

}

const迭代器已经可以正常运行

一个const迭代器 和一个普通的迭代器 两者之间非常的冗余 而且有很大的相似程度 这里我们可以用一个类来同时实现两个迭代器的功能

可以通过增加模版参数来实现

template<class T, class ref, class ptr>
class ListIterator//这里也存在私有的问题 需要使用public
{
public://这里也不用太担心公有化访问的 问题 不同的编译器下底层实现的代码是不同的 也就是名字不一定相同 同时正常一般是直接回调用迭代器 不会直接访问类成员
	typedef ListNode<T> Node;
	typedef ListIterator<T,ref,ptr> Self;
	Node* _node;

	ListIterator(Node* node)
		:_node(node)
	{}
	//这个迭代器要不要写析构 不要  这个迭代器指向的空间是链表的 不希望被析构
	//这里也不需要拷贝构造 因为这里需要的就是浅拷贝 所以默认生成的拷贝构造就够用  所以默认生成的浅拷贝也是有些意义的 并不是完全没意义
	ref operator * () //每个类都能重载运算符 当重载号运算符之后就能控制一个类的行为
	{
		return _node->_data;
	}
	ptr operator->() 
	{
		return & _node->_data;
	}
	Self& operator ++()
	{
		_node = _node->_next;
		return *this;
	}

	Self& operator --()
	{
		_node = _node->_prev;
		return *this;
	}

	Self  operator ++(int)//这里是后置加加 参数中有int用来与前置++作区分
	{
		Self tmp(*this);//这里返回的是加加之前的值的 所以需要提前保存一下
		_node = _node->_next;
		return *tmp;
	}

	Self  operator --(int)
	{
		Self tmp(*this);
		_node = _node->_prev;
		return *tmp;
	}

	bool operator !=(const Self& it)
	{
		return _node != it._node;
	} 

	bool operator ==(const Self& it)
	{
		return _node == it._node;
	}
};

同时在list类中typedef时分别用不用的参数类型来使用不同的迭代器功能  通过模版参数 给不同的模版参数 让编译器来给我们写两个类(实例化)


typedef ListIterator<T ,T&,T*> iterator; //每个类都要使用iterator去规范迭代器  这样每个容器使用的都是iterator 这样使用起来方便 成本较低
//初始化
typedef ListIterator<T,const T&,const T*> const_iterator;
//typedef ListIterator<const T> const_iterator; 这种写法是不可取的 当这样写时虽然迭代器中的符合const迭代器的要求 但是我们链表中的Node是还是T 而迭代器中的是const T 在两者类型不用 实参是无法传给形参的
		

这样测试用例也是可以正常运行

最后在来模拟一下insert 和erase 

链表的insert 通过 对pos位置的结点 和 pos位置的prev指向节点之间插入一个新节点

这里创立prev节点 和cur节点 指针     prev指向cur指向节点的prev指向节点  cur指向pos位置的节点    同时有newnode作为新插入节点的指针  首先prev节点的next指向newnode  newnode节点的prev指向prev节点  newnode节点的next指向cur的结点  cur节点的prev 指向newnode节点

这时就插入成功了 

且这里的insert是没有迭代器失效的 但是库中仍然给了返回值

iterator insert (iterator pos , const T&x)
{
	Node* cur = pos._node;//这里pos指向的迭代器没有改变 所以没有失效  但是库中仍然返回了一个值 
	Node* prev = cur->_prev;
	Node* newnode = new Node(x);

	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = cur;
	cur->_prev = newnode;
	return iterator(newnode);
}

erase函数则是通过创建 prev和next指针分别指向 pos位置上一个节点 和下一个结点来进行连接

同时将now指向的pos位置的节点通过delete进行删除  这里存在迭代器失效  且只有pos位置的迭代器失效 其他位置正常  这里通过给返回值返回删除前下一个位置 来更新迭代器  这里通过断言 防止将头结点删掉

iterator erase(iterator pos)//这里存在迭代器失效的问题 
{
	assert(pos != end());
	Node* now = pos._node;
	Node* prev = now->_prev;
	Node* next = now->_next;
	prev->_next = next;
	next->_prev = prev;

	delete now;
	return iterator(next);
}

之后通过代码复用 通过insert 和erase可以快速写出 push_back  push_front  pop_back pop_front

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

	tail->_next = newnode;
	newnode->_prev = tail;
	newnode->_next = _head;
	_head->_prev = newnode;*/
	insert(end(),x);
}
void pop_back()//持续的删除是有可能会将哨兵位给删掉的 所以要加断言  断言在erase函数
{
   
	erase(--end());
}

void push_front(const T& x)
{
	insert(begin(),x);
}
void pop_front()
{
	erase(begin());
}

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

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

相关文章

OpenHarmony(鸿蒙南向开发)——小型系统内核(LiteOS-A)【时间管理】

往期知识点记录&#xff1a; 鸿蒙&#xff08;HarmonyOS&#xff09;应用层开发&#xff08;北向&#xff09;知识点汇总 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ 子系统开发内核 轻量系统内核&#xff08;LiteOS-M&#xff09; 轻量系统内核&#…

围剿Model Y,小米SUV也来拼刺刀了

文 | AUTO芯球 作者 | 雷慢 马斯克真是被小米雷军盯上了&#xff0c; 前面小米SU7死磕Model 3&#xff0c; 现在小米SUV又来打Model Y了&#xff0c; 别不信啊&#xff0c;就刚刚&#xff0c;小米SUV出现了最大的曝光&#xff0c; 外观谍照&#xff0c;内饰中控台都曝光了…

RflySim工具链常见问题答疑

1. RflySim结合硬件能不能实现无人机颜色巡线呢&#xff1f; 可以&#xff0c;内置有一个通过相机识别来攻击小球的实验&#xff0c;可见&#xff1a;【RflySim安装路径】\RflySimAPIs\8.RflySimVision\1.BasicExps\1-VisionCtrlDemos\e3_ShootBall&#xff0c;不过要想实现无人…

Linux 进程3

进程地址空间 CPU读取数据都需要地址&#xff0c;在计算机中所有东西都是一种数据&#xff0c;包括我们的进程。 这是一个进程空间示意图&#xff0c;操作系统通过task_struct结构体链表来管理每一个进程&#xff0c;结构体里面有一个指针指向操作系统为进程开辟的一段空间&am…

博导团队指导、解读实验结果、SCI论文润色

表观组&#xff1a; DAP-seq:转录因子-DNA互作研究工具 ATAC-seq :染色质开放程度研究工具 H3K4me3 ChIP-seq:组蛋白甲基化修饰工具 BS-seq :DNA甲基化研究工具 H3K27ac ChIP-seq:组蛋白乙酰化修饰研究工具 Cut&Tag:转录因子研究工具 ChIP-seq:转录因子-DNA互作工具 互作组…

HTTP 教程

HTTP/HTTPS 简介 HTTP&#xff08;Hypertext Transfer Protocol&#xff0c;超文本传输协议&#xff09;和 HTTPS&#xff08;Hypertext Transfer Protocol Secure&#xff0c;超文本传输安全协议&#xff09;是用于在网络中传输信息的两种主要协议。它们定义了客户端和服务器…

PDF样本册如何分享到朋友圈

​想象一下&#xff0c;你刚刚参加了一场行业盛会&#xff0c;获取了一份包含最新行业动态、优秀案例的PDF样本册。你迫不及待地想要分享给身边的朋友&#xff0c;与他们共同学习、探讨。然而&#xff0c;传统的分享方式要么依赖纸质版&#xff0c;要么通过电子邮件&#xff0c…

C++模拟实现list:list、list类的初始化和尾插、list的迭代器的基本实现、list的完整实现、测试、整个list类等的介绍

文章目录 前言一、list二、list类的初始化和尾插三、list的迭代器的基本实现四、list的完整实现五、测试六、整个list类总结 前言 C模拟实现list&#xff1a;list、list类的初始化和尾插、list的迭代器的基本实现、list的完整实现、测试、整个list类等的介绍 一、list list本…

LeetCode讲解篇之220. 存在重复元素 III

文章目录 题目描述题解思路题解代码 题目描述 题解思路 我们可以考虑存储数组中连续indexDiff个数字&#xff0c;这样我们只需要在这连续的indexDiff个数字中查找相差小于等于valueDiff的两个数字的问题 对于该查找问题&#xff0c;我们可以考虑使用以valueDiff大小为一个桶&a…

自动化测试常用函数

目录 一、元素的定位 1、cssSelector 2、xpath &#xff08;1&#xff09;xpath 语法 1、获取HTML页面所有的节点 2、获取HTML页面指定的节点 3、获取一个节点中的直接子节点 4、获取一个节点的父节点 5、实现节点属性的匹配 6、使用指定索引的方式获取对应的节点内容…

MYSQL面试知识点手册

第一部分&#xff1a;MySQL 基础知识 1.1 MySQL 简介 MySQL 是世界上最流行的开源关系型数据库管理系统之一&#xff0c;它以性能卓越、稳定可靠和易用性而闻名。MySQL 主要应用在 Web 开发、大型互联网公司、企业级应用等场景&#xff0c;且广泛用于构建高并发、高可用的数据…

动态线程池(四)

动态线程池 dtp生命周期管理 生命周期相关类图 DtpExecutor EagerEtpExecutor OrderedDtpExecutor TaskWrapper任务包装器 MdcRunnable TaskWrappers NotifyEnum NoticeManager通知管理器 InvokerChain调用链

AI与量化投资人才培养计划-连接职场 助力走在金融行业前沿

AI与量化投资人才培养计划-连接职场 助力走在金融行业前沿 人工智能&#xff08;AI&#xff09;的快速发展&#xff0c;量化投资已逐渐成为金融行业的新趋势&#xff0c;对专业人才的需求日益迫切。本文将深入探讨一项针对AI与量化投资的人才培养计划&#xff0c;旨在为金融专业…

No operations allowed after statement closed

错误信息&#xff1a; The last packet successfully received from the server was 3,576,246 milliseconds ago. The last packet sent successfully to the server was 3,576,247 milliseconds ago. 参考解决方案 https://github.com/alibaba/druid/issues/5549 如果修改…

誉龙视音频 Third/TimeSyn 远程命令执行复现

0x01 漏洞描述&#xff1a; 誉龙公司定位为系统级的移动视音频记录解决方案提供商&#xff0c;凭借其深厚的行业经验&#xff0c;坚持自主研发&#xff0c;匠心打造记录仪领域行业生态&#xff0c;提供开放式的记录仪APK、GB28181 SDK、国网B协议、管理平台软件OEM。誉龙视音频…

leaflet加载GeoServer的WMS地图服务.md

leaflet加载GeoServer的WMS地图服务&#xff0c;该示例涵盖了涵盖了 “WMS图层加载、WMS图层动态投影、图层index顺序调整、图层添加、高德地图、腾讯地图OpenStreet地图”&#xff0c;WMS图层加载看代码中标注的核心代码部分即可。 <!DOCTYPE html> <html xmlns&qu…

湖南(用户访谈)源点咨询 市场调研中何种情况下选择定性方式?

湖南&#xff08;市场调研&#xff09;源点咨询认为&#xff0c;很多调研方法被分组为"定性调研方法"或"收集资料的定性方法"。 这反映了对定性调研的继承&#xfe63;&#xfe63;它的根源在于社会科学&#xff0c;尤其在社会学和人类学&#xff0c;还有…

AI大模型之旅-langchain结合glm4,faiss构建本地知识库

所需依赖如下&#xff1a; _libgcc_mutex0.1main _openmp_mutex5.11_gnu accelerate0.34.2pypi_0 aiofiles23.2.1pypi_0 aiohappyeyeballs2.4.0pypi_0 aiohttp3.10.5pypi_0 aiosignal1.3.1pypi_0 annotated-types0.7.0pypi_0 anyio4.4.0pypi_0 attrs24.2.0pypi_0 bitsandbytes…

Web开发:ABP框架3——入门级别的接口增删改查实现原理

一、上节回顾 运用了ABP框架&#xff0c;使用了EFcore进行增删改查 二、程序的入口 代码解说&#xff1a; public class Program // 定义程序主类 {public async static Task<int> Main(string[] args) // 主方法&#xff0c;返回状态码{// 配置Serilog日志Log.Logger…

如何给zip文件设置自动加密,保护压缩包不被随意打开

ZIP是日常生活和工作中经常用到的压缩文件格式&#xff0c;对于重要的文件&#xff0c;我们往往还会设置打开密码&#xff0c;保护压缩包不被随意打开。 如果每次压缩文件都要设置一次密码&#xff0c;操作久了还是有点麻烦&#xff0c;那有没有一种方法&#xff0c;只要压缩文…