list容器

LIST容器

list容器基本概念
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成,每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
相对于vector的连续性空间,list就显得负责许多,它的好处是每次插入或者删除一个元素,就是配置或者释放一个元素的空间,因此,list对于空间的运用有绝对精准,一点也不浪费,而且,对于任何位置的元素插入或者元素的移除,list永远是常数时间。
list和vector是两个最常被使用的容器。
list容器是一个双向链表。
采用动态存储分配,不会造成内存浪费和溢出
链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
链表灵活,但是空间和时间额外耗费较大
list容器的迭代器
list容器不能像vector一样以普通指针作为迭代器,因为其结点不能保证在同一块连续的内存空间上,list迭代器必须有能力指向list结点,并有能力进行正确的递增、递减、取值、成员存取操作。所谓list正确的递增、递减、取值、成员存取是指,递增时指向下一个结点,递减时指向上一个结点,取值时取得是结点的数据值,成员取用时取得是结点的成员。
由于list是一个双向链表,迭代器必须具备前移、后移的能力,所以list容器提供的就是Bidirectional Iterators。
list有一个重要的性质,插入操作和删除操作都不会造成原有的list迭代器失效,这在vector中是不成立的,因为vector的插入操作可能造成记忆体重新配置,导致原有的迭代器全部失效,甚至list元素的删除,也只有被删除的那个元素的迭代器失效,其他迭代器不受影响。
list容器的数据结构
list容器不仅是一个双向链表,而且还是一个循环的双向链表。

//An highlighted block
//list是双向循环链表
void test1()
	{
		
		list<int>myList;
		for(int i=0;i<10;i++)
			{
			myList.push_back(i);
			}
		list<int>::_Nodeptr node = myList._Myhead->_Next;
		for(int i=0;i<myList._Mysize*2;i++)
			{
			cout << "Node:" << node->_Myval << endl;
			node = node->_Next;
			if(node == myList._Myhead)
				{
				node = node ->_Next;
				}
			}
	}

在这里插入图片描述
list常用API

list构造函数
listlstT; //list采用模板类实现,对象的默认构造形式
list(beg,end); //构造函数将(beg,end)区间中的元素拷贝给本身
list(n,elem); //构造函数将n个elem拷贝给本身
list(const list &lst); //拷贝构造函数

list数据元素插入和删除操作
push_back(elem); //在容器尾加入一个元素
pop_back(); //删除容器中的最后一个元素
push_front(elem); //在容器开头插入一个人元素
pop_front(); //从容器开头移除第一个元素
insert(pos,elem); //在pos位置插入elem元素的拷贝,返回新数据的位置
insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值
insert(pos,beg,end); //在pos位置插入(beg,end)区间的数据,无返回值
clear(); //移除容器的所有数据
erase(beg,end); //删除(beg,end)区间的数据,返回下一个数据的位置
erase(pos); //删除pos位置的数据,返回下一个数据的位置
remove(elem); //删除容器中所有与elem值匹配的元素
unique(elem); //移除连续的重复元素,只留下其中的第一个。 unique() 函数使用 == 运算符比较连续元素。可以在对元素进行排序后,再使用 unique(),这样可以保证移除序列中全部的重复元素。

//An highlighted block
void print(list<int>&L)
	{
	for(list<int>::iterator it = L.begin();it != L.end();it++)
		{
			cout << *it << " ";
		}
	cout << endl;
	}
void test2()
	{
		list<int>L(10,10);
		list<int>L2(L.begin(),L.end());

		print(L);
		print(L2);

		L2.push_back(100);

		//逆序打印
		for(list<int>::reverse_iterator it = L2.rbegin();it !=L2.rend();it++)
			{
				cout << *it << " ";
			}
		cout << endl;


		//list迭代器不支持随机访问
		list<int>::iterator itBegin = L2.begin();
		//itBegin = itBegin +1;			报错

		//插入数据
		list<int>L3;
		L3.push_back(10);
		L3.push_back(30);
		L3.push_back(40);
		L3.push_front(55);
		L3.push_back(20);
		L3.push_front(100);
		print(L3);

		//删除两端数据
		L3.pop_front();
		L3.pop_back();
		print(L3);

		//插入数据
		L3.insert(L3.begin(),1000);
		print(L3);

		//删除数据
		L3.push_back(10);
		L3.push_back(10);
		L3.remove(10);		//删除所有的10
		print(L3);

	}

在这里插入图片描述list大小操作
size(); //返回容器中元素的个数
empty(); //判断容器是否为空
resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除
resize(num,elem); //重新指定容器的长度为num,若容器变长,则以elem填充新位置,如果容器变短,则末尾超出容器长度的元素被删除

list赋值操作
assign(beg,end); //将(beg,end)区间的数据拷贝赋值给本身
assign(n,elem); //将n个elem拷贝赋值给本身
list &operator = (const list &lst); //重载等号操作符
swap(lst); //与lst本身的元素互换

list数据存取
front(); //返回第一个元素
back(); //返回最后一个元素

//An highlighted block
void test3()
	{
		list<int>L3;
		L3.push_back(10);
		L3.push_back(30);
		L3.push_back(40);
		L3.push_front(55);
		L3.push_back(20);
		L3.push_front(100);

		cout << "大小:" << L3.size() << endl;
		if(L3.empty())
			{
				cout << "L3为空" << endl;
			}
		else
			{
				cout << "L3不为空" << endl;
			}
		L3.resize(10);
		print(L3);

		L3.resize(3);
		print(L3);

		list<int>L4;
		L4.assign(L3.begin(),L3.end());
		cout << "front:" << L4.front() << endl;
		cout << "back:" << L4.back() << endl;
	
	}

在这里插入图片描述list反转排序
reverse(); //反转链表,比如lst包含1,3,5元素,运行此方法后,lst就包含5,3,1元素
sort(); //list排序 默认从小到大
sort() 函数模板定义在头文件 algorithm 中,要求使用随机访问迭代器。但 list 容器并不提供随机访问迭代器,只提供双向迭代器,因此不能对 list 中的元素使用 sort() 算法。但是,还是可以进行元素排序,因为 list 模板定义了自己的 sort() 函数。sort() 有两个版本:无参 sort() 函数将所有元素升序排列。第二个版本的 sort() 接受一个函数对象或 lambda 表达式作为参数,这两种参数都定义一个断言用来比较两个元素。

//An highlighted block
void test4()
	{
		list<int>L;
		L.push_back(10);
		L.push_back(20);
		L.push_back(40);
		L.push_back(30);

		L.reverse();
		print(L);
		//所有不支持随机访问的迭代器,不可以用系统提供的算法
		//如果不支持用系统算法,那么这个类内会提供
		//sort(L.begin(),L.end());
		L.sort();
		print(L);			//从小到大

	}

在这里插入图片描述

//An highlighted block
//自定义数据类型
class Person
	{
	public:
		Person(string name,int age,int height)
			{
				this ->m_Name = name;
				this ->m_Age = age;
				this ->m_Height = height;
			}
		//重载==让remove可以删除自定义的person类型
		bool operator == (const Person& p)
			{
			if( this ->m_Age == p.m_Age && this ->m_Height == p.m_Height && this ->m_Name == p.m_Name )
				return true;
			return false;
			}
		
		string m_Name;
		int m_Age;
		int m_Height;
	};
//Person排序规则 如果年龄相同 则按照身高升序排序
bool myComparePerson(Person &p1,Person&p2)
	{
	//if(p1.m_Age > p2.m_Age)
	//	{
	//		return true;
	//	}
	//return false;

	if(p1.m_Age == p2.m_Age)
		{
		return p1.m_Height < p2.m_Height;
		}
	else
		{
		return p1.m_Age > p2.m_Age;
		}

	}
void test5()
	{
		list<Person>L;
		Person p1("德玛",27,190);
		Person p2("阿木木",6,100);
		Person p3("薇恩",27,170);
		Person p4("提莫",11,99);
		Person p5("炮手",18,98);

		L.push_back(p1);
		L.push_back(p2);
		L.push_back(p3);
		L.push_back(p4);
		L.push_back(p5);

		//需求 打印数据时按照年龄降序输出
		//对于自定数据类型,必须要指定排序规则
		//L.sort(myComparePerson);
		for(list<Person>::iterator it = L.begin();it != L.end();it++)
			{
			cout << "姓名:" << it ->m_Name << " 年龄:" << it ->m_Age << "身高:" << it ->m_Height << endl;
			
			}
		cout << endl;
		//删除p4
		L.remove(p4);
		for(list<Person>::iterator it = L.begin();it != L.end();it++)
			{
			cout << "姓名:" << it ->m_Name << " 年龄:" << it ->m_Age << "身高:" << it ->m_Height << endl;
			
			}
		cout << endl;

	

	}

list 的成员函数 merge() 以另一个具有相同类型元素的 list 容器作为参数。两个容器中的元素都必须是升序。参数 list 容器中的元素会被合并到当前的 list 容器中。例如:

std::list<int> my_values {2, 4, 6, 14};
std::list<int> your_values{ -2, 1, 7, 10};
my_values.merge (your_values);//my_values contains: -2 1 2 4 6 7 10 14
your_values.empty(); // Returns true

元素从 your_values 转移到 my_values,因此,在执行完这个操作后,your_values 中就没有元素了。改变每个 list 节点的指针,在适当的位置将它们和当前容器的元素链接起来,这样就实现了元素的转移。list 节点在内存中的位置不会改变;只有链接它们的指针变了。在合并的过程中,两个容器中的元素使用 operator()() 进行比较。

在另一个版本的 merge() 函数中,可以提供一个比较函数作为该函数的第二个参数,用来在合并过程中比较元素。例如:

std::list<std::string> my_words { "three","six", "eight"};
std::list<std::string> your_words { "seven", "four", "nine"};
auto comp_str = [](const std::strings s1, const std::strings s2){ return s1[0]<s2[0];};
my_words.sort (comp_str); //"eight" "six" "three"
your_words.sort (comp_str) ;  //"four" "nine" "seven"
my_words.merge (your_words, comp_str) ; // "eight" "four" "nine" "six" "seven" "three"

这里的字符串对象比较函数是由 lambda 表达式定义的,这个表达式只比较第一个字符。比较的效果是,在合并的 list 容器中,"six”在”seven”之前。在上面的代码中,也可以无参调用 merge(),这样"seven"会在"six"之前,这是一般的排序。

splice() 的第一个参数是指向目的容器的迭代器。第二个参数是元素的来源。第三个参数是一个指向源list容器中被粘接元素的迭代器,它会被插入到第一个参数所指向位置之前。代码执行完中后,容器中的内容如下:

your_words: "seven", "nine"
my_words : "three", "four", "six", "eight"

当要粘接源 list 容器中的一段元素时,第 3 和第 4 个参数可以定义这段元素的范围。 例如:

your_words.splice(++std::begin(your_words),my_words,++std::begin(my_words), std::end(my_words));

上面的代码会将 my_words 从第二个元素直到末尾的元素,粘接到 your_words 的第二个元素之前。上面两个 list 容器的内容如下:

your_words:"seven", "four", "six", "eight","nine" my_words: "three"

下面的语句可以将 your_words 的全部元素粘接到 my_words 中:

my_words.splice(std::begin(my_words), your_words);

your_words 的所有元素被移到了 my_words 的第一个元素"three”之前。然后,your_words 会变为空。即使 your_words 为空,也仍然可以向它粘接元素:

your_words.splice(std::end(your_words), my_words);

现在,my_words 变为空,your_words 拥有全部元素。第一个参数也可以是 std::begin (your_words),因为当容器为空时,它也会返回一个结束迭代器。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值