【C++初阶】第12课—list

发布于:2025-03-30 ⋅ 阅读:(90) ⋅ 点赞:(0)


   相信学过string和vector容器后,对于list应该游刃有余了,接下来我将带大家将list的大致框架过一遍,然后来模拟实现下list。


  • 首先介绍下list,list其实就是C++实现的一个带头节点的双向链表,当然如果想了解单链表的话,也可以学习forward_list

1. list的构造

构造函数 接口说明
list(size_t n, const size_type& val) 用n个val值来构造list对象
list(Inputiterator first, Inputiterator last) 用迭代器区间构造list对象
list(const list& lt) 用list对象lt来构造list对象

在这里插入图片描述


2. list迭代器的常见接口

2.1 list遍历的迭代器接口

迭代器接口 说明
begin() 指向list对象中的第一个数据
end() 指向list对象最后一个数据的下个位置
rbegin() 指向反向迭代器的第一个数据
rend() 指向反向迭代器的最后一个元素的前一个位置
empty() 判断list对象是否为空
size() 返回list对象有效数据个数
front() 返回list对象的第一个数据
back() 返回list对象的最后一个数据

在这里插入图片描述


2.2 list修改数据的迭代器接口

迭代器接口 说明
assign() 为list容器重新分配内容,并相应修改容器容量大小
push_back() 尾插数据
emplace_back() 尾部插入数据
pop_back() 尾删数据
push_front() 头插数据
pop_front() 头删数据
emplace_front() 头部插入数据
insert() 指定位置插入数据
erase() 删除指定位置数据
swap() 交换两个list对象里面的数据和size
clear() 删除list里面的所有数据,size置为0

在这里插入图片描述


  • 当然assign()也可以使用迭代器进行初始化
  • 需要注意的是,C++实现的迭代器分为3种:单向迭代器、双向迭代器、随机迭代器
  • 单向迭代器只支持++不支持 - -
  • 双向迭代器支持++和 - -
  • 随机迭代器只支持++、 - - 、+ 、 -
  • 恰好list模版实现时就是双向迭代器

在这里插入图片描述


  • push_back()emplace_back()的区别

在这里插入图片描述


在这里插入图片描述


  • pop_back()、pop_front()删除数据

在这里插入图片描述


在这里插入图片描述


  • 由于list模版中没有实现find函数查找,因此需要调用算法库实现的find()函数模版

在这里插入图片描述


2.3 list排序、逆序、合并相关操作的成员函数

迭代器接口 说明
splice 将另一个列表(或同一列表的另一个部分)的元素移动到当前列表的指定位置
remove 删除容器中等于val值的元素
reverse 将list对象的数据逆序排列
merge 合并
sort 排序(使用的归并排序)
unique 删除list中重复且相邻的数据,只保留一个
  • list里面的remove(const value_type& val)会删除list对象中所有等于val值的数据

在这里插入图片描述


  • reverse()逆序

在这里插入图片描述


在这里插入图片描述


  • 其实这里有个疑问需要解释下:C++算法库里面实现有sort()排序模版,为什么这里还要实现sort呢?
  • 这是因为算法库实现的sort容器是随机迭代器版本,对于链表来说不支持随机访问,只能从头遍历,因此list实现了一个sort排序的接口,主要利用归并排序可以很好的解决这个问题

在这里插入图片描述


在这里插入图片描述


  • merge接口:用来合并两个list对象,并且会将合并的list对象置size为空
  • list中merge使用时,需要注意两个list是已经排好序的,否则无法使用

在这里插入图片描述


  • splice接口:在给定的迭代器位置插入list对象

在这里插入图片描述


在这里插入图片描述


  • unique:删除重复且相邻数据,仅保留一个

在这里插入图片描述


3. 模拟实现list

3.1 模拟实现list的构造

在这里插入图片描述


3.2 模拟实现list的尾插

在这里插入图片描述


3.3 模拟实现迭代器iterator

  • list是双向链表,链表的每个节点都是单独存在的,因此链表不是连续的物理空间
  • list的每个节点都保存了指向上个节点和下个节点的指针,分别为prev、next,另外每个节点还存储了数据data
  • 由于list不是连续的物理空间,实现list时就不能使用typedef T* iterator这样的方式,因为迭代器++时找不到下个节点的位置
  • 由于迭代器指的是具有像指针一样行为的容器,那么就可以将迭代器单独封装为一个类
  • 链表的结构图
    在这里插入图片描述

在这里插入图片描述


  • list除了存储一些内置类型的数据,有时还会存储自定义类型的数据
  • 而自定义类型的对象往往不支持流插入操作,这就回导致迭代器访问时出现报错

在这里插入图片描述


  • 解决办法1

在这里插入图片描述


  • 解决办法2

在这里插入图片描述


  • const修饰的迭代器版本
//const修饰的迭代器
template<class T>
struct list_const_iterator
{
	typedef list_node<T> Node;
	Node* node;

	//迭代器的构造函数
	list_const_iterator(Node* _node)
		:node(_node)
	{}

	//解引用操作符*重载
	const T& operator*()
	{
		return node->data;
	}

	//解引用操作符->重载
	const T* operator->()
	{
		return &node->data;
	}

	//++运算符重载
	list_const_iterator<T>& operator++()
	{
		node = node->next;
		return *this;
	}
	//!=运算符重载
	bool operator!=(const list_const_iterator<T>& it)
	{
		return node != it.node;
	}
	//==运算符重载
	bool operator==(const list_const_iterator<T>& it)
	{
		return node == it.node;
	}
};
  • const修饰的迭代器本身可以修改,但是指向的内容是不可以修改的
  • const迭代器使用

在这里插入图片描述


在这里插入图片描述


  • 合并普通迭代器和const修饰的迭代器,使用迭代器模版

在这里插入图片描述


  • 前置++(或–)和后置++(或–)的实现

在这里插入图片描述
前置++是先++后再使用,因此是++走向下个节点后返回自身的引用;而后置++是先使用再++,因此是先返回自身再走向下个节点,需要创建临时对象tmp来保存节点更改前的迭代器
这里需要注意的是,为了区分前后置++,默认给后置++的形参参数为int,这里int仅做区分使用。由于前置++不需要创建临时对象,因此对比之下更高效


3.4 模拟实现list的插入删除

  • insert(指定位置)插入和erase(指定位置)删除

在这里插入图片描述


  • 头插尾插和头删尾删

在这里插入图片描述


3.5 模拟实现list的析构和clear

在这里插入图片描述


3.6 模拟实现list的深拷贝

在这里插入图片描述


  • 赋值运算符重载

在这里插入图片描述


  • 多参数初始化构造

在这里插入图片描述


  • 初始化后

在这里插入图片描述


4. 代码

  • 头文件代码
#pragma once

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

namespace TT
{
	template<class T>
	struct list_node
	{
		list_node<T>* prev;
		list_node<T>* next;
		T data;

		//构造初始化
		list_node(const T& x = T())
			:prev(nullptr)
			,next(nullptr)
			,data(x)
		{ }
	};

	template<class T,class Ref,class Ptr>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator<T, Ref, Ptr> Self;
		Node* node;

		//迭代器的构造函数
		list_iterator(Node* _node)
			:node(_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& it)
		{
			return node != it.node;
		}
		//==运算符重载
		bool operator==(const Self& it)
		{
			return node == it.node;
		}
	};

	//const修饰的迭代器
	//template<class T>
	//struct list_const_iterator
	//{
	//	typedef list_node<T> Node;
	//	Node* node;

	//	//迭代器的构造函数
	//	list_const_iterator(Node* _node)
	//		:node(_node)
	//	{}

	//	//解引用操作符*重载
	//	const T& operator*()
	//	{
	//		return node->data;
	//	}

	//	//解引用操作符->重载
	//	const T* operator->()
	//	{
	//		return &node->data;
	//	}

	//	//++运算符重载
	//	list_const_iterator<T>& operator++()
	//	{
	//		node = node->next;
	//		return *this;
	//	}
	//	//!=运算符重载
	//	bool operator!=(const list_const_iterator<T>& it)
	//	{
	//		return node != it.node;
	//	}
	//	//==运算符重载
	//	bool operator==(const list_const_iterator<T>& it)
	//	{
	//		return node == it.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;

		iterator begin()
		{
			return iterator(head->next);
		}

		iterator end()
		{
			return iterator(head);
		}

		const_iterator begin() const 
		{
			return const_iterator(head->next);
		}

		const_iterator end() const
		{
			return const_iterator(head);
		}

		size_t size()
		{
			return _size;
		}

		size_t size() const
		{
			return _size;
		}

		void empty_init()
		{
			head = new Node;
			head->prev = head;
			head->next = head;
			_size = 0;
		}

		//构造
		list()
		{
			empty_init();
		}

		//多参数初始化
		list(initializer_list<T> lt)
		{
			empty_init();
			for (auto& e : lt)
			{
				push_back(e);
			}
		}

		//深拷贝--->lt2(lt1)
		list(const list<T>& lt)
		{
			empty_init();
			for (auto e : lt)
			{
				push_back(e);
			}
		}

		//swap交换
		void swap(const list<T>& lt)
		{
			std::swap(head, lt.head);
			std::swap(_size, lt._size);
		}

		//赋值运算符重载--->lt3 = lt1
		list<T>& operator=(const list<T> lt)
		{
			swap(lt);
			return *this;
		}

		//尾插
		void push_back(const T& x)
		{
			//Node* newnode = new Node(x);
			//Node* tail = head->prev;
			//newnode->prev = tail;
			//tail->next = newnode;
			//newnode->next = head;
			//head->prev = newnode;

			insert(end(), x);
		}
		//头插
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		//头删
		void pop_front()
		{
			erase(begin());
		}
		//尾删
		void pop_back()
		{
			erase(--end());
		}
		//pos位置插入数据
		void insert(iterator pos, const T& x)
		{
			Node* cur = pos.node;
			Node* prev = cur->prev;
			Node* newnode = new Node(x);
			prev->next = newnode;
			newnode->prev = prev;
			newnode->next = cur;
			cur->prev = newnode;
			++_size;
		}
		//erase删除指定位置节点
		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos.node;
			Node* PrevNode = cur->prev;
			Node* NextNode = cur->next;

			PrevNode->next = NextNode;
			NextNode->prev = PrevNode;
			delete cur;
			--_size;
			return iterator(NextNode);
		}

		//析构
		~list()
		{
			clear();
			delete head;
			head = nullptr;
		}

		//clear清除
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
	private:
		Node* head;
		size_t _size;
	};
}
  • 后缀cpp代码
#include "list.h"
#include<algorithm>

struct A
{
	A(int a1 = 1, int a2 = 1)
		:_a1(a1)
		,_a2(a2)
	{ }

	int _a1;
	int _a2;
};

void list_test1()
{
	TT::list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);

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

void Print(const TT::list<A>& lt)
{
	TT::list<A>::const_iterator it = lt.begin();
	while (it != lt.end())
	{
		//it->_a1 += 1;
		cout << (*it)._a1 << " " << (*it)._a2 << endl;;
		++it;
	}
	cout << endl;
}

void list_test2()
{
	TT::list<A> lt1;
	lt1.push_back({ 1,1 });
	lt1.push_back({ 2,2 });
	lt1.push_back({ 3,3 });
	lt1.push_back({ 4,4 });

	TT::list<A>::iterator it1 = lt1.begin();
	while (it1 != lt1.end())
	{
		//cout << (*it1)._a1 << ":" << (*it1)._a2 << endl;
		it1->_a1 += 1;
		cout << it1->_a1 << ":" << it1->_a2 << endl;
		++it1;
	}
	cout << endl;

	Print(lt1);
}

void list_test3()
{
	TT::list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);

	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;
	lt1.push_back(78);
	lt1.push_back(99);
	lt1.push_front(100);
	lt1.push_front(300);
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;
	lt1.pop_back();
	lt1.pop_front();
	for (auto e : lt1)
	{
		cout << e << " ";
	}
	cout << endl;
}

void test_list4()
{
	TT::list<int> lt1;
	lt1.push_back(1);
	lt1.push_back(2);
	lt1.push_back(3);
	lt1.push_back(4);

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

	TT::list<int> lt2(lt1);

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

	TT::list<int> lt3 = lt2;
	for (auto e : lt3)
	{
		cout << e << " ";
	}
	cout << endl;

	TT::list<int> lt4 = { 10,20,30,40 };
	for (auto e : lt4)
	{
		cout << e << " ";
	}
	cout << endl;
}

int main()
{
	//list_test1();
	//list_test2();
	//list_test3();
	test_list4();
	return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到