list队列的使用和模拟实现基于c++

1. list的介绍及使用

1.1 list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向
    其前一个元素和后一个元素。
  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高
    效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率
    更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list
    的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间
    开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这
    可能是一个重要的因素)

1.2 list常见的接口使用

1.2.1 list的构造
构造函数( (constructor))接口说明
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
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> v;
	list<int> v1(10, 5);
	list<int> v2(v1);
	list<int> v3(v1.begin(), v1.end());
	return 0}

在这里插入图片描述

1.2.2 list iterator的使用

此处,大家可暂时将迭代器理解成一个指针,该指针指向list中的某个节点。

函数声明接口说明
begin + end返回list第一个元素的迭代器+返回list最后一个元素下一个位置的迭代器
rbegin + rend返回list最后一个元素的迭代器+返回第一个元素的上一个位置
  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动。
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动。

代码演示:

#include<iostream>
#include<list>
using namespace std;
int main()
{
	list<int> v;
	v.push_back(1);//尾插
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);
	
	auto it = v.begin();
	while (it != v.end())
	{
		cout << *it;
		it++;
	}
	cout << endl;

	auto it2 = v.rbegin();
	while (it2 != v.rend())
	{
		cout << *it2;
		it2++;
	}
	
	
	cout << endl;
	return 0;
}

在这里插入图片描述

1.2.3 list capacity
函数声明接口说明
size返回list中有效节点的个数
empty检测list是否为空,是返回true,否则返回false

代码演示:

#include<iostream>
#include<list>
using namespace std;
int main()
{
	list<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);

if (v.empty() == false)
{
	cout << "no empty";
}
else if (v.empty() == true)
{
	cout << "empty";
}
cout << endl;
cout << v.size();
return 0;
}

在这里插入图片描述

1.2.4 list element access
函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

代码演示:

#include<iostream>
#include<list>
using namespace std;
int main()
{
	list<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
v.push_back(5);
v.push_back(6);

v.back() = 9;
v.front() = 0;
cout << v.back()<<endl;
cout << v.front();
return 0;
}

在这里插入图片描述

1.2.5 list modifiers
函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
insert在list position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

代码演示:

#include<iostream>
#include<list>
using namespace std;
template<class T>
void PrintfList(const list<T>&l)
{
	for (auto e : l)
	{
		cout << e << " ";
	}
	cout << endl;
}

int main()
{
	list<int> v(3,0);//000
list<int> s(3, 'a');//97 97 97 
v.push_front(1);// 1000  ;push_front	在list首元素前插入值为val的元素
PrintfList(v);
v.push_back(3);//10003	;push_back	在list尾部插入值为val的元素
PrintfList(v);
v.pop_back();//1000 ;pop_back	删除list中最后一个元素
PrintfList(v);
v.pop_front();//000 ;//pop_front	删除list中第一个元素
PrintfList(v);
v.insert(++v.begin(),2);//0200 insert	在list position 位置中插入值为val的元素
PrintfList(v);
v.erase(++v.begin());//000  erase	删除list position位置的元素
PrintfList(v);
v.swap(s);
PrintfList(v);//97 97 97 swap	交换两个list中的元素
v.clear();//nullptr clear
PrintfList(v);
return 0;
}

在这里插入图片描述

2. list的模拟实现

2.1 list的类

template<class T>
	class list
	{
	public:
		typedef ListNode<T> node;
	private:
		node* head;
		size_t _size = 0;
	};
	template<class T>
	struct ListNode
	{
		typedef ListNode<T> node;
		node* _next;//指向该节点下一个node节点。
		node* _prev;//指向该节点上一个node节点。
		T _date;//存放数据
		ListNode(T v=T())//list l(v)v为T类型的数据时,生成的l的T _date为v。
			:_next(nullptr),
			_prev(nullptr),
			_date(v)
		{
		}	
	};
	创建一个list类,里面成员为node* head(用来存放数据,为list的哨兵位,哨兵位不存放数据,从第二个no
de开始存放)和_size(list数据大小);其中node为一个结构体。

2.2 list的begin和end


		//begin end
		iterator begin()
		{
			return head->_next;
		}
		iterator end()
		{
			return head;
		}
		const_iterator begin()const
		{
			return head->_next;
		}
		const_iterator end()const
		{
			return head;
		}

在这里插入图片描述

list中的head头节点下一个节点为begin,最后一个存放数据的节点下一个节点为end,也就是head节点。这里我们写了iterator和const_iterator两种,函数后加const可构成函数重载,cont list调用const函数,普通list调用的是非const函数。

2.3 list迭代器的模拟

2.3.1 list迭代器模拟代码

在这里插入图片描述
这里我们实现一个iterator类存放指向list中的节点。

template<class T,class ref,class ptr>	
	struct  ListIterator
	{
		typedef ListNode<T> node;
		typedef ListIterator<T,ref,ptr> iterator;

		node* _node;//iterator中的成员为一个node* 指针指向list节点。

		ListIterator(node* nd)
			:_node(nd)//调用析构函数,使iterator中_node指向list地址和nd一样的节点。
		{
		}
		iterator& operator++()
		{
			_node = _node->_next;//迭代器指向_node的next节点,这里++是前置重载。
			return *this;
		}
		iterator operator++(int)//这里(int)为后置++的重载符号。
		{
			iterator tmp(_node);
			_node = _node->_next;
			return tmp;
		}
		iterator& operator--()//迭代器指向_node的prev节点,这里--是前置重载。
		{
			_node = _node->_prev;
			return *this;
		}
		iterator operator--(int)//这里(int)为--的后置重载符号。
		{
			iterator tmp(_node);
			_node = _node->_prev;
			return tmp;

		}
		ref operator * ()//解引用,返回节点内的T date,节点内存放的值。
		{
			return _node->_date;
		}
		ptr operator -> ()
		{
			return &_node->_date;
		}
		bool operator !=(const iterator&r)
		{
			return _node != r._node;//判断两个迭代器存放list节点地址是否不相同。
		}
		bool operator ==(const iterator& r)//判断两个迭代器存放list节点地址是否相同。
		{
			return _node == r._node;
		}
	};

2.3.2 迭代器中的实现细节
  1. template<class T,class ref,class ptr>:这个模板中ref为T&,ptr为T*,因为iterator中大部分符号重载时,const_iterator和iterator的返回值是一样的,只有operator *和operator ->两个是不同的,const_iterator的operator *返回的是const T&,iterator的是T&;const_iterator的operator ->返回的是const T*,iterator的是T*。 因此正常情况下我们需要给list和const_list写两套iterator,这里我们只需要利用模板实例化两套iterator就可以实现了。
template<class T>
	class list
	{
	public:
		typedef ListNode<T> node;
		typedef ListIterator<T,T&,T*> iterator;//在这里实例化一个非const的iterator。
		typedef ListIterator<T,const T&,const T*> const_iterator;
		//在这里实例化一个const的iterator。
		iterator begin()
		{
			return head->_next;
		}
		iterator end()
		{
			return head;
		}
		const_iterator begin()const
		{
			return head->_next;
		}
		const_iterator end()const
		{
			return head;
		}
	private:
		node* head;
		size_t _size = 0;
	};
  1. 如何理解这个重载?
ptr operator -> ()
		{
			return &_node->_date;
		}

在这里插入图片描述
我们自定义了一个类型aa如图,我们这样调用it->a1时,先重载,返回一个&_node->_date,也就是aa类型,然后这里代码就应该是aa a2,很明显代码很难理解,而这里编译器并不是这样运行的。真正的运行逻辑是:这里的代码其实是it.operator->()->a1,这里就是先返回aa类型的地址,然后再用箭头访问aa中的数据。

2.4 list的初始化

  1. 创建一个list,首先要先创建list的head:
void empty_init()
{
	head = new node;
	head->_next = head;
	head->_prev = head;
}

让头节点的下一个和上一个都指向自己。

  1. list()
list()
{
	empty_init();
};

创建一个只有头节点的list。

  1. list(int n, const T& value = T()
list(int n, const T& value = T())
{
	empty_init();//创建头节点
	while (n != 0)
	{
		push_back(value);//尾插,后面实现。
		n--;
	}
};

创建一个数量为n,内容为value的list。

  1. list(Iterator first, Iterator last)
template <class Iterator>
list(Iterator first, Iterator last)
{
	empty_init();
	while (first != last)
	{
		insert(end(), *first);
		first++;
	}
};

拷贝一个list的[first,last)这个迭代器区间的内容。

  1. list(const list& l)
list(const list<T>& l)
{
	empty_init();
	const_iterator it = l.begin();
	while (it != l.end())
	{
		push_back(it._node->_date);
		it++;
	}
};

拷贝队列l,初始化list。

初始化代码:

list<int> l1;

list<int> l2(5,9);

list<int> l3(l2.begin(),l2.end());

list<int> l4(l3);

2.5 list的插入删除

  1. iterator insert(iterator pos,const T& v)
iterator insert(iterator pos,const T& v)
{
	node * newnode = new node(v);
	node* prev = pos._node->_prev;
	node* next = pos._node;
	prev->_next = newnode;
	newnode->_prev = prev;
	next->_prev = newnode;
	newnode->_next = next;
	_size++;
	return pos;
}

在迭代器pos指向的位置前插入一个数据,并返回pos迭代器,pos迭代器指向的内容不变。

  1. iterator erase(iterator pos)
iterator erase(iterator pos)
{
	node* next = pos._node->_next;
	node* prev = pos._node->_prev;
	prev->_next = next;
	next->_prev = prev;
	delete pos._node;
	_size--;
	return next;
}

删除迭代器pos指向的节点,返回pos指向节点的下一个节点。

  1. void push_back( T v)和void push_front(T v)
void push_back( T v)
{
	insert(end(),v);
}
void push_front(T v)
{
	insert(begin(), v);
}

头插和尾插。

  1. void pop_back()和void pop_front()
void pop_back()
{
	erase(end()._node->_prev);
}
void pop_front()
{
	erase(begin());
}

尾删和头删。

2.6 list的析构函数

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

	}
};
~list()
{
	clear();
	delete head;
	head = nullptr;
	_size = 0;

};

先调用clear函数将list中的除head节点外所有节点内存释放,然后再将头节点释放。

2.7 list的交换和赋值

  1. void swap(list&l)
void swap(list&l)
{
	std::swap(head,l.head);
	std::swap(_size,l._size);
}

调用库里面的swap使两个list中2各个对象指向的指针直接交换。

  1. list& operator=(list<T> l)
list<T>& operator=(list<T> l)
{
	swap(l);
	return *this;
};

将一个list的值赋值list,这里使(list<T> l)是传的值,这里拷贝构造一个临时的list<T> l,然后让它和需要被赋值的list内容交换,出函数后,临时的l,会调用析构函数,将被赋值list原来的内容释放掉。

2.8 list的大小和是否为空
size_t size()
{
	return _size;
}
bool empty()
{
	return _size == 0;
}

size()返回list中有效节点个数,empty()如果节点只有head节点,则为空,返回true,否则返回false。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

aaa最北边

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值