c++|list使用及深度剖析模拟实现

news2025/2/23 23:52:42

目录

一、list介绍与使用

 1.1 list介绍

1.2 list的使用 

1.2.1list的构造 

 1.2.2iterator

1.2.3容量 

 1.2.4元素访问

1.2.5 元素修改

二、list的深度剖析及模拟实现

三、list与vector的对比


一、list介绍与使用

 1.1 list介绍

①list底层是带头双向循环链表,在任意位置可进行插入和删除的序列式容器。

②list与其他序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好

③与其他序列式容器相比,list的缺陷是不支持任意位置的随机访问,要访问某个元素,必须从已知位置迭代到访问位置,这段迭代则需要额外的线性时间开销,途中,还需要开辟额外空间,来保存每个节点的相关联信息。

1.2 list的使用 

list同样也是一个模板类,有了前面的string、vector基础,这里也不在对list的接口做过多解释,而是简单的进行介绍使用一些常见的重要接口,然后再来对其底层进行一个分析与模拟实现。

1.2.1list的构造 

构造函数接口说明
list(size_type n,const value_type& val = value_type())n个val值构造list
list()构造空list
list(const list& x)拷贝构造
list(InputIterator first,InputIterator last)用[first,last)区间中的元素构造list
#include <iostream>
#include <list>
using namespace std;

int main()
{

	list<int> lt1;//构造空list

	list<double> lt2(3, 2.1);//用3个2.1值构造lt2
	for (auto e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;
	list<double> lt3(lt2);//拷贝构造,用lt2构造lt3
	for (auto e : lt3)
	{
		cout << e << " ";
	}
	cout << endl;
	list<double> lt4(lt3.begin(), lt3.end());//用迭代器区间中的元素构造lt4
	for (auto e : lt4)
	{
		cout << e << " ";
	}
	cout << endl;
	return 0;
}

 

 1.2.2iterator

函数声明接口说明
begin+end返回第一个元素的迭代器+返回最后一个元素的下一个位置的迭代器
rbegin+rend返回第一个元素的reverse_iterator,即最后一个元素的下一个位置+返回最后一个元素的reverse_iterator,即第一个元素的位置

 

#include <iostream>
#include <list>
using namespace std;

int main()
{

	list<char> ch(5,'a');

	list<char>::iterator cit = ch.begin();
	list<char>::reverse_iterator rcit = ch.rbegin();

	while (cit != ch.end())
	{
		cout << *cit;
		cit++;
	}
	cout << endl;
	while (rcit != ch.rend())
	{
		cout << *rcit;
		rcit++;
	}
	
	return 0;
}

 

1.2.3容量 

函数声明接口说明
empty检测list是否为空,是返回true,否返回false
size返回list中有效节点的个数
#include <iostream>
#include <list>
using namespace std;

int main()
{

	list<char> ch(5, 'a');

	cout << boolalpha<< ch.empty() << endl;//以bool类型的方式打印,而不是以数字方式
	cout << ch.size() << endl;

	return 0;
}

 

 1.2.4元素访问

函数声明接口说明
front返回list的第一个节点值的引用
back返回list的最后一个节点值的引用
#include <iostream>
#include <list>
using namespace std;

int main()
{

	list<string> ch(5,"a");
	

	cout << ch.front() << endl;
	cout << ch.back() << endl;
	

	return 0;
}

 

1.2.5 元素修改

函数声明接口说明
push_front头插,在list首元素插入一个值
pop_front头删,删除list首元素
push_back尾插,在尾部插入一个值
pop_back尾删,删除尾部元素
insert在迭代器POS位置插入元素,或者一段区间的值
erase删除迭代器POS位置的值,或者一段区间的值
swap交换两个list中的元素
clear清空list中的有效元素

 

int main()
{

	list<string> ch(5,"a");
	

	ch.push_front("b");//头插
	ch.push_back("c");//尾插

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

	ch.pop_front();//头删
	ch.pop_back();//尾删
	for (auto e : ch)
	{
		cout << e;
	}
	cout << endl;
	
	
	ch.insert(ch.begin(), "d");//迭代器指向开始位置,头插
	ch.insert(ch.end(), ch.begin(), ch.end());//迭代器指向末尾,尾插一段区间的值
	for (auto e : ch)
	{
		cout << e;
	}
	cout << endl;

	ch.erase(ch.begin());//迭代器指向开始位置,头删

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

	
	list<string> s(3,"s");
	s.swap(ch);//交换
	for (auto e : ch)
	{
		cout << e;
	}
	cout << endl;
	for (auto e : s)
	{
		cout << e;
	}
	cout << endl;

	s.clear();//清空元素
	ch.clear();
	cout << s.empty() << endl;
	cout << ch.empty() << endl;
	return 0;
}

 

二、list的深度剖析及模拟实现

相较于string、vector,list的模拟实现对于迭代器的实现进行了另一层封装,为什么要这样做?

首先,对于,string、vector而言,他们的底层都是连续的,那么,他们对于迭代器的实现,本质上就是对于指针的封装,正因为底层是连续的,所以指针就可以进行加加到下一个连续的地址,然而对于list而言,底层并不是连续的,如果再对迭代器的实现是对指针的封装,想想是不合适的,因为对于链表而言,底层不连续,指针加加,一定会到下一个节点吗?不一定

那么对于list的迭代器可以怎么实现呢?

我们知道,链表访问下一个节点,是用当前节点的next到下一个节点,那么,迭代器的加加,就可以通过重载函数对链表访问下一个节点的方式进行封装,所以,对于list的迭代器的实现,就是对链表的实现玩法进行一个封装。光看这里还是不能理解,那么接下来就对其如何实现进行一个模拟实现。


①在实现list前,我们先得要有一个节点类,用来存放指针,数据

template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;
		ListNode(const T& x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};

 

② 接下来就可以实现list的底层,首先对于迭代器的实现就可以单独进行一个封装,我们学习过string、vector、list的迭代器玩法,知道迭代器是属于这些容器的类域,对于list容器而言,迭代器实现成了一个类,要使迭代器属于list的类域,只需在list类中使用迭代器类即可,那么对于自定义类型,编译器会去自动调用其成员函数。



template<class T>
	class _list_iterator
	{
		typedef ListNode<T>* pNode;
		typedef _list_iterator<T> Self;
	public:
		pNode _node;
	 public:
		 _list_iterator(pNode node = nullptr)
		 {
			 _node = node;
		 }
		 _list_iterator(const Self& x)
		 {
			 _node = x._node;
		 }
		 T& operator*()
		 {
			 return _node->_data;
		 }
		 T* operator->()
		 {
			 return &(_node->_data);
		 }
		 Self& operator++()
		 {
			 
			 _node = _node->_next;
			 return *this;
		 }
		 Self operator++(int)
		 {
			 Self tmp(*this);
			 
			 _node = _node->_next;
			 return tmp;
		 }
		 Self& operator--()
		 {
			 
			 _node = _node->_prev;
			 return *this;
		 }
		 Self operator--(int)
		 {
			 Self tmp(*this);
			 _node = _node->_prev;
			 return tmp;
		 }

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

	};


template<class T>
	class list
	{
		typedef ListNode<T> Node;
		typedef Node* pNode;
	public:
		typedef _list_iterator<T> iterator;
	
		
	public:
		//迭代器
		iterator begin()//自定义类型成员,编译器会去调用它的成员函数
		{
			return _head->_next;//即在返回值时,会构造迭代器对象,去调用迭代器的构造函数
		}
		iterator end()
		{
			return _head;
		}

        //构造
		list()
		{
			_head = new Node;
			_head->_next = _head->_prev = _head;
		}
		list(int n, const T& val = T())
		{
			_head = new Node;
			_head->_next = _head->_prev = _head;
			pNode cur = _head;
			while (n--)
			{
				pNode node = new Node(val);
				

				cur->_next = node;
				_head->_prev = node;

				node->_next = _head;
				node->_prev = cur;
				cur = node;
			}
		}
		template<class T>
		list(iterator first, iterator last)
		{
			pNode cur = _head;
			while (first != last)
			{
				pNode node = new Node;
				node->_data = *first;

				cur->_next = node;
				_head->_prev = node;

				node->_next = _head;
				node->_prev = cur;
				cur = node;
				first++;
			}
		}

		list(const list<T>& x)
		{

			pNode cur = x._head->_next;
			_head = new Node;
			pNode _cur = _head;

			while (cur != x._head)
			{
				pNode node = new Node;
				node->_data = cur->_data;

				_cur->_next = node;
				_head->_prev = node;

				node->_next = _head;
				node->_prev = _cur;
				_cur = node;

				cur = cur->_next;
			}
		}
		list<T>& operator=(const list<T> l)
		{
			if(*this != &l)
				swap(l);
			return *this;
		}
		~list()
		{
			pNode cur = _head->_next;
			pNode next = cur;
			while (cur != _head)
			{
				next = next->_next;
				delete cur;
				cur = next;
			}
			_head->_next = _head->_prev = _head;
		}
private:
    Node* _head;
}

上述代码迭代器的实现就是对于链表的玩法进行封装,通过重载函数进行一个包装。其实透过代码还是比较难以理解,接下来再结合图进行解释。

对于list对象,其成员_head属于自定义类型,类型是节点类,编译器会去调用自定义类型的成员函数,在构造list对象时调用list对应的构造函数,此时就会创建节点,如下图:

 

 构造迭代器对象时,本质上就是其成员_node指向了list对象的成员_head所指向的结点_head->_next,那么是如何指向的,如下图:

那么结合以上两点,迭代器对象的成员_node 指向和list对象的_head成员指向就可以用一张图来解释

 

 ③返向迭代器+list剩余函数实现

其实反向迭代器的实现跟正向迭代器的形式是一样的,不同的就是指向问题,反向迭代器可以通过

正向迭代器来实现,改变其指向即可如下:

template<class Iterator>
	struct _list_reverse_iterator
	{

		Iterator _it;
		typedef _list_reverse_iterator<Iterator> Self;
		
		_list_reverse_iterator(Iterator it)
			:_it(it)
		{}
		Iterator& operator*()
		{
			Iterator tmp(_it);
			return *(--tmp);
		}
		Iterator* operator->()
		{

			return &(operator*());
		}
		Self& operator++()
		{

			_it--;
			return *this;
		}
		Self operator++(int)
		{
			Self tmp(*this);

			_it--;
			return tmp;
		}
		Self& operator--()
		{

			_it++;
			return *this;
		}
		Self operator--(int)
		{
			Self tmp(*this);
			_it++;
			return tmp;
		}

		bool operator!=(const Self& x)
		{
			return _it != x._it;
		}
		bool operator==(const Self& x)
		{
			return _it == x._it;
		}
	};
template<class T>
	class list
	{
		typedef ListNode<T> Node;
		typedef Node* pNode;
	public:
		typedef _list_iterator<T> iterator;

		typedef _list_reverse_iterator<iterator> reverse_iterator;

	public:
        reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

        ...
    private:
        Node* _head;
}

对于迭代器的const成员,修饰的主要是不能对重载函数的修改,那么又要加上模板参数,为了达到一个巧妙,对于迭代器的实现可以挂几个模板参数,那么对于迭代器的实例化,就可以实例成自己想要的类型。

#include <iostream>
#include <assert.h>
using namespace std;

namespace bite
{
	//节点类
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;
		ListNode(const T& x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};
	//template<class T, class ref, class ptr>
	//class _list_reverse_iterator
	//{
	//	typedef ListNode<T>* pNode;
	//	typedef _list_reverse_iterator<T, ref, ptr> Self;
	//public:
	//	pNode _node;
	//public:
	//	_list_reverse_iterator(pNode node = nullptr)
	//	{
	//		_node = node;
	//	}
	//	_list_reverse_iterator(const Self& x)
	//	{
	//		_node = x._node;
	//	}
	//	ref operator*()
	//	{
	//		return _node->_prev->_data;
	//	}
	//	ptr operator->()
	//	{
	//		return &(_node->_prev->_data);
	//	}
	//	Self& operator++()
	//	{

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

	//		_node = _node->_prev;
	//		return tmp;
	//	}
	//	Self& operator--()
	//	{

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

	//	bool operator!=(const Self& x)
	//	{
	//		return _node != x._node;
	//	}
	//	bool operator==(const Self& x)
	//	{
	//		return _node == x._node;
	//	}
	//};
	template<class T,class ref,class ptr>
	class _list_iterator
	{
		typedef ListNode<T>* pNode;
		typedef _list_iterator<T,ref,ptr> Self;
	public:
		pNode _node;
	 public:
		 _list_iterator(pNode node = nullptr)
		 {
			 _node = node;
		 }
		 _list_iterator(const Self& x)
		 {
			 _node = x._node;
		 }
		 ref operator*()
		 {
			 return _node->_data;
		 }
		 ptr operator->()
		 {
			 return &(_node->_data);
		 }
		 Self& operator++()
		 {
			 
			 _node = _node->_next;
			 return *this;
		 }
		 Self operator++(int)
		 {
			 Self tmp(*this);
			 
			 _node = _node->_next;
			 return tmp;
		 }
		 Self& operator--()
		 {
			 
			 _node = _node->_prev;
			 return *this;
		 }
		 Self operator--(int)
		 {
			 Self tmp(*this);
			 _node = _node->_prev;
			 return tmp;
		 }

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

	};




	template<class Iterator, class Ref, class Ptr>
	struct _list_reverse_iterator
	{

		Iterator _it;
		typedef _list_reverse_iterator<Iterator, Ref, Ptr> Self;
		
		_list_reverse_iterator(Iterator it)
			:_it(it)
		{}
		Ref operator*()
		{
			Iterator tmp(_it);
			return *(--tmp);
		}
		Ptr operator->()
		{

			return &(operator*());
		}
		Self& operator++()
		{

			_it--;
			return *this;
		}
		Self operator++(int)
		{
			Self tmp(*this);

			_it--;
			return tmp;
		}
		Self& operator--()
		{

			_it++;
			return *this;
		}
		Self operator--(int)
		{
			Self tmp(*this);
			_it++;
			return tmp;
		}

		bool operator!=(const Self& x)
		{
			return _it != x._it;
		}
		bool operator==(const Self& x)
		{
			return _it == x._it;
		}
	};
	template<class T>
	class list
	{
		typedef ListNode<T> Node;
		typedef Node* pNode;
	public:
		typedef _list_iterator<T,T&,T*> iterator;
		typedef _list_iterator<T, const T&,const T*> const_iterator;
		typedef _list_reverse_iterator<iterator, T&, T*> reverse_iterator;
		typedef _list_reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
	public:
		//构造
		list()
		{
			_head = new Node;
			_head->_next = _head->_prev = _head;
		}
		list(int n, const T& val = T())
		{
			_head = new Node;
			_head->_next = _head->_prev = _head;
			pNode cur = _head;
			while (n--)
			{
				pNode node = new Node(val);
				

				cur->_next = node;
				_head->_prev = node;

				node->_next = _head;
				node->_prev = cur;
				cur = node;
			}
		}
		template<class T>
		list(iterator first, iterator last)
		{
			pNode cur = _head;
			while (first != last)
			{
				pNode node = new Node;
				node->_data = *first;

				cur->_next = node;
				_head->_prev = node;

				node->_next = _head;
				node->_prev = cur;
				cur = node;
				first++;
			}
		}

		list(const list<T>& x)
		{

			pNode cur = x._head->_next;
			_head = new Node;
			pNode _cur = _head;

			while (cur != x._head)
			{
				pNode node = new Node;
				node->_data = cur->_data;

				_cur->_next = node;
				_head->_prev = node;

				node->_next = _head;
				node->_prev = _cur;
				_cur = node;

				cur = cur->_next;
			}
		}
		list<T>& operator=(const list<T> l)
		{
			if(*this != &l)
				swap(l);
			return *this;
		}
		~list()
		{
			pNode cur = _head->_next;
			pNode next = cur;
			while (cur != _head)
			{
				next = next->_next;
				delete cur;
				cur = next;
			}
			_head->_next = _head->_prev = _head;
		}

		//迭代器
		iterator begin()
		{
			return _head->_next;
		}
		iterator end()
		{
			return _head;
		}
		const_iterator begin() const
		{
			return _head->_next;
		}
		const_iterator end() const
		{
			return _head;
		}

		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}
		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}
		const_reverse_iterator rbegin() const
		{
			return const_reverse_iterator(end());
		}
		const_reverse_iterator rend() const
		{
			returnconst_reverse_iterator(begin());
		}


		//容量
		size_t size() const
		{
			size_t count = 0;
			pNode cur = _head->_next;
			while (cur != _head)
			{
				cur = cur->_next;
				count++;
			}
			return count;
		}

		bool empty() const
		{
			return _head == _head->_next && _head == _head->_prev;
		}


		//元素获取
		T& front()
		{
			return _head->_next->_data;
		}
		const T& front() const
		{
			return _head->_next->_data;
		}
		T& back()
		{
			return _head->_prev->_data;
		}
		const T& back() const
		{
			return _head->_prev->_data;
		}

		//元素修改
		void push_back(const T& val)
		{
			insert(end(), val);
			
		}
		void pop_back()
		{
			/*assert(_head->_next != _head && _head->_prev != _head);
			pNode node = _head->_prev;
			_head->_prev = node->_prev;
			node->_prev->_next = _head;
			delete node;*/
			erase(--end());
		}

		void push_front(const T& val)
		{
			insert(begin(), val);
		}
		void pop_front()
		{
			/*assert(_head->_next != _head && _head->_prev != _head);
			pNode node = _head->_next;
			_head->_next = node->_next;
			node->_next->_prev = _head;
			
			delete node;*/
			erase(begin());
		}
		iterator insert(iterator pos, const T& val)
		{
			

			pNode cur = pos._node;
			pNode prev = cur->_prev;

			pNode node = new Node(val);
			node->_next = prev->_next;
			prev->_next = node;

			cur->_prev = node;
			node->_prev = prev;
			return node;
		}
		iterator erase(iterator pos)
		{
			assert(pos != end());

			pNode cur = pos._node;
			pNode next = cur->_next;
			
			cur->_prev->_next = next;
			next->_prev = cur->_prev;
			
			delete cur;
			
			return next;
		}
		void clear()
		{
			pNode cur = _head->_next;
			pNode next = cur;
			while (cur != _head)
			{
				next = next->_next;
				delete cur;
				cur = next;
			}
			_head->_next = _head->_prev = _head;
		}
		void swap(list<T> l)
		{
			std::swap(_head, l._head);
		}
	private:
		Node* _head;
	};

	void listtest()
	{
		list<int> i(3, 2);
		for (auto e : i)
		{
			cout << e;
		}
		cout << endl;
		list<int>::iterator it = i.begin();
		while (it != i.end())
		{
			cout << *it;
			it++;
		}
		cout << endl;
		cout << i.size() << endl;
		cout << i.empty() << endl;

		list<int> j = i;
		for (auto e : i)
		{
			cout << e;
		}
		cout << endl;
	}
	void listtest1()
	{
		list<string> s(1, "a");
		s.push_back("c");
		s.push_back("d");
		s.push_back("c");
		s.push_back("c");

		list<string>::reverse_iterator it = s.rbegin();
		while (it != s.rend())
		{
			cout << *it;
			it++;
		}
		cout << endl;
	}
}
int main()
{
	bite::listtest();
	bite::listtest1();

	return 0;
}

 

三、list与vector的对比

vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

vector

list

底层结构动态顺序表,一段连续空间带头节点双向循环链表
随机访问支持随机访问,时间复杂度为O(1)不支持随机访问,时间复杂度为O(N)
插入和删除任意位置插入和删除效率低,需要移动元素,时间复杂度为O(N),插入时可能需要增容,则需开辟新空间,将元素拷贝过去,效率低任意位置插入和删除效率高,不需要移动元素,时间复杂度为O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点为动态开辟的空间,不连续,容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生态指针(例如,char*,int*,string*等)对原生态指针(节点指针)进行封装
迭代器失效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,导致原来迭代器失效,删除元素时,当前迭代器需要重新赋值否则会失效插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关系随机访问

 

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

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

相关文章

Redis进阶——BitMap用户签到HyperLogLog实现UV统计

目录 用户签到实现签到功能 签到统计HyperLogLog实现UV统计UV和PV的概述测试百万数据的统计 用户签到 BitMap功能演示 我们针对签到功能完全可以通过MySQL来完成&#xff0c;例如下面这张表 用户签到一次&#xff0c;就是一条记录&#xff0c;假如有1000W用户&#xff0c;平…

RCE漏洞及其绕过——[SWPUCTF 2021 新生赛]easyrce、caidao、babyrce

目录 什么是Shell 1、Shell简介 2、印刷约定 一、什么是RCE 漏洞产生条件&#xff1a; 漏洞检测&#xff1a; 1.远程命令执行 system()函数&#xff1a; passthru()函数&#xff1a; exec()函数&#xff1a; 无回显 shell_exec()函数&#xff1a; 2.远程代码执行 e…

我们一起看看《看漫画学C++》中如何讲解对象的动态创建与销毁

《看漫画学C》这本书中会用图文的方式生动地解释对象的动态创建与销毁。在C中&#xff0c;动态创建对象是通过new运算符来实现的&#xff0c;而销毁对象则是通过delete运算符来完成的。这种方式可以让程序在需要时分配内存给对象&#xff0c;并在对象不再需要时释放内存&#x…

「Word 论文排版」插入分节符导致word转PDF后出现空白页

问题 word转PDF后出现空白页 解决 但是此方法会让页面页脚标记出错 TODO 如下图所示 在论文目录后有一个分节符&#xff0c;转成PDF之后就多了一个空白页 文件-打印-页面设置-选中封面那一页-版式-从偶数页开始 再导出空白页就没了

Nginx莫名奇妙返回了404

描述 nginx作为反向代理&#xff0c;代理python的服务&#xff0c;但是通过代理访问服务的时候&#xff0c;报了404的错误。 难受的是客户现场没有查看日志的权限&#xff0c;只有查看配置文件的权限&#xff0c;我们检测了几遍配置文件也没有找到问题&#xff0c;哎~ 问题引…

34. 【Android教程】菜单:Menu

作为 Android 用户&#xff0c;你一定见过类似这样的页面&#xff1a; 它就是我们今天的主角——菜单&#xff0c;它的使用场景和作用不用多说&#xff0c;几乎每个 App 都会用到它&#xff0c;今天我们就一起来看看 Android 提供的几种菜单类型及用法。 1. 菜单的几种类型 根…

[Algorithm][滑动窗口][无重复字符的最长字串][最大连续的一个数 Ⅲ][将x减到0的最小操作数]详细讲解

目录 1.无重复字符的最长字串1.题目链接2.算法原理详解3.代码实现 2.最大连续的一个数 Ⅲ1.题目链接2.算法原理详解3.代码实现 3.将x减到0的最小操作数1.题目链接2.算法原理详解3.代码实现 1.无重复字符的最长字串 1.题目链接 无重复字符的最长字串 2.算法原理详解 研究的对…

美化博客文章(持续更新)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;游戏实现&#xff1a;贪吃蛇​​​​​​ &#x1f337;追光的人&#xff0c;终会万丈光芒 前言&#xff1a; 该文提供我的一些文章设计的一些方法 目录 1.应用超链接 1.应用超链接

URL GET +号后台接收成空格

问题&#xff1a;参数spdmwhbs001 其中包含URL特殊符号 如果用GET请求方式不做任何不处理那么浏览器自动将转为%20 请求链接为 details?spdmwhbs%20001&limitKcysType1 后台接收到的参数为 whbs 001 &#xff0c;自动将号转成空格了。 尝试解决&#xff08;失败&#…

使用FastDDS编译IDL文件

1.安装FastDDS环境 Ubuntu22.04 1.1安装依赖的软件 sudo apt-get update //基础工具安装 sudo apt install cmake g python3-pip wget git //Asio 是一个用于网络和低级 I/O 编程的跨平台C库&#xff0c;它提供了一致的 异步模型。 TinyXML2是一个简单&#xff0c;小巧&…

400电话如何对接配置SIP

400电话对接配置SIP的基本步骤 要配置400电话对接SIP&#xff0c;通常需要遵循以下基本步骤&#xff1a; 注册和认证&#xff1a;首先需要在相应的云通信平台上注册账号&#xff0c;并进行企业实名认证。 开通语音服务&#xff1a;在通过认证后&#xff0c;需要开通语音服务&…

C# winform s7.net expected 22 bytes.”

S7.Net.PlcException:“Received 12 bytes: 32-02-00-00-00-00-00-00-00-00-81-04, expected 22 bytes.” 原因是博图的连接机制未勾选

Java编程题 | 数组元素交换

大家可以关注一下专栏&#xff0c;方便大家需要的时候直接查找&#xff0c;专栏将持续更新~ 题目描述 编写一个Java程序&#xff0c;输入一个整数数组&#xff0c;将最大的元素与第一个元素交换&#xff0c;最小的元素与最后一个元素交换&#xff0c;然后输出修改后的数组…

数据结构从入门到实战——顺序表的应用

目录 一、基于动态顺序表实现通讯录 二、代码实现 2.1 通讯录的初始化 2.2 通讯录的销毁 2.3 通讯录的展示 2.4 通讯录添加联系人信息 2.5 通讯录删除联系人信息 2.6 通讯录修改联系人信息 2.7 通讯录的查找联系人信息 2.8 将通讯录中联系人信息保存到文件中 2.9…

【Qt】设置QT标准对话框为中文字体

设置QT标准对话框为中文字体 一、问题二、解决方法1、找到Qt内置的翻译文件 qt_zh_CN.qm2、在代码中加载该文件 一、问题 在Qt中我们使用的标准对话框都是英文&#xff0c;例如下面的 字体选择对话框&#xff0c;但是实际中我们需要构建的是中文对话框。 所以我们需要使用Qt官…

【redis】hash和list常用命令

hash类型 Redis自身已经是键值对结构了。Redis自身的键值对就是通过哈希的方式来组织的。 把key这一层组织完成之后,到了value这一层。value的其中一种类型还可以再是哈希。哈希类型中的映射关系通常称为field-value,用于区分Redis整体的键值对(key-value)。注意这里的 value…

为什么创业总是小部分人能成功?

每年都有大批量应届生等待就业&#xff0c;同时&#xff0c;也有一大批大龄中青人正在面临失业。在这种焦虑&#xff0c;内卷的氛围下&#xff0c;就会激发一波又一波的人走上创业的道路。想靠创业改变命运。辞职之后&#xff0c;第一时间能想到的创业方式要么就是就是 开个奶茶…

阿里云ECS服务器安装docker

首先查看阿里云ECS的服务器的版本 cat /etc/redhat-release如果是Alibaba Cloud Linux release 3,请执行以下命令 添加docker-ce的dnf源。 sudo dnf config-manager --add-repohttps://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo安装Alibaba Cloud Linux 3专…

【若依前后端分离】仪表盘绘制

示例&#xff1a; 代码&#xff1a; InstrumentPanel.vue组件 <template><div><!-- 在这里放置你的图表组件 --><div ref"echarts" style"width: 100%; height: 400px;"></div></div> </template><script&g…

如何将你的iOS应用成功上架App Store(图文详解)

上架基本需求资料 1、苹果开发者账号&#xff08;如还没账号先申请- 苹果开发者账号申请教程&#xff09; 2、开发好的APP 通过本篇教程&#xff0c;可以学习到ios证书申请和打包ipa上传到appstoreconnect.apple.com进行TestFlight测试然后提交审核的完整流程&#xff01; …