0
点赞
收藏
分享

微信扫一扫

Python数据容器——列表(list)

目录

前言

重要接口实现

框架

默认成员函数

迭代器(重点)

1.引言

2.list迭代器类实现 

3.list类中调用实现 

增删查改

后记


前言

        我们知道,stl中的vector对应数据结构中的顺序表,string类对应字符串,而今天要讲的list类对应带头双向链表,并不是对应单链表,带头双向链表的基本操作在数据结构课程中已经学过,所以今天即将要讲的常见接口并不是重点,重点是list的迭代器的实现。

        我们也知道,string、vector的迭代器就是原生指针,如果使用原生指针可以实现list的迭代器吗?答案是不行,因为list的数据并不是一块连续的存储空间,无法像指针那样取访问元素,但是为了保持所有容器迭代器的使用一致,我们如何实现list的迭代器才能像原生指针那样通过++、--去控制,这里就体现出了封装的重要性,快往下看看吧!

重要接口实现

  • 框架

代码:

template <class T>
struct ListNode
{
	//构造函数用来创节点
	ListNode(const T& x = T())
		:_data(x)
		, _prev(nullptr)
		, _next(nullptr)
	{

	}

	T _data;
	ListNode<T>* _prev;
	ListNode<T>* _next;
};

template <class T>
class List
{
	typedef ListNode<T> Lnode;  //作用:下面写ListNode<T>较麻烦,typedef一下,使用Lnode较方便

public:

    //...

private:
	Lnode* _head;
};
  • 默认成员函数

代码:

	//创建并初始化头节点,放于构造函数的最前面
	void empty_init()
	{
		_head = new Lnode();
		_head->_next = _head->_prev = _head;
	}

    //构造函数
	List()
	{
		empty_init();
	}

	//普通拷贝构造函数
	List(const List& L)   //注意:类外必须是List<类型名>,类里可以是List,但建议List<T>
	{
		empty_init();
		auto it = L.begin();
		while (it != L.end())
		{
			push_back(*it);
			it++;
		}
	}

	void swap(List<T>& L)
	{
		std::swap(_head, L._head);
	}

	//传迭代器区间构造函数
	template <class InputIterator>  
	List(InputIterator first, InputIterator last)
	{
		empty_init();

		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

    //拷贝构造函数
	//现代写法
	List(const List<T>& L)
	{
		empty_init();
		List<T> tmp(L.begin(), L.end());
		swap(tmp);
	}

    //赋值运算符重载
	//现代写法
	List<T>& operator=(const List<T>& L)
	{
		List<T> tmp(L.begin(), L.end());
		swap(tmp);
		return *this;
	}
	//更狠的现代写法
	List<T>& operator=(List<T> L)   //直接传一个拷贝过来,相当于上面的tmp,函数结束自动释放
	{
		swap(L);
		return *this;
	}

    //清除除了头节点之外所有的节点
   	void clear()
	{
		auto it = begin();
		while (it != end())
		{
			it = erase(it);
		}
	}

    //析构函数
	~List()
	{
		clear();
		_head->_next = _head->_prev = nullptr;
		delete _head;
		_head = nullptr;
	}
  • 迭代器(重点)

1.引言

2.list迭代器类实现 

1)框架 

代码:

template <class T, class Ref, class Ptr>
struct __list_iterator
{
    //注意:这两个typedef只是因为ListNode<T>、__list_iterator<T, Ref, Ptr>很麻烦写,所以简化一下,也方便理解
	typedef ListNode<T> Lnode;
	typedef __list_iterator<T, Ref, Ptr> iterator;

    //构造函数
    __list_iterator(Lnode* p)
		:_node(p)
	{

	}

    //...

	Lnode* _node;   //链表中的迭代器表示节点的指针
};

2) 关系运算符重载

代码:

    bool operator==(const iterator& it) const
	{
		return _node == it._node;
	}
	bool operator!=(const iterator& it) const
	{
		return !(*this == it);
	}

 3)运算符++、--重载

代码:

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

4)操作符*重载

代码:

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

 5)操作符->重载

代码:

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

 测试:

3.list类中调用实现 

 代码:

    typedef __list_iterator<T, T&, T*> iterator;  //普通迭代器类
	typedef __list_iterator<T, const T&, const T*> const_iterator;   //const迭代器类

	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);
	}
  • 增删查改

代码:

	iterator insert(iterator pos, const T& x)
	{
		Lnode* newNode = new Lnode(x);
		pos._node->_prev->_next = newNode;
		newNode->_prev = pos._node->_prev;
		newNode->_next = pos._node;
		pos._node->_prev = newNode;

		return iterator(newNode);  //返回插入位置的迭代器
	}
    
    //尾插
	void push_back(const T& x)
	{
		insert(end(), x);
	}

    //头插
	void push_front(const T& x)
	{
		insert(begin(), x);
	}

	iterator erase(iterator pos)
	{
		assert(pos != end());
		Lnode* tmp = pos._node->_next;
		pos._node->_prev->_next = pos._node->_next;
		pos._node->_next->_prev = pos._node->_prev;
		delete pos._node;
		return iterator(tmp);
	}

    //尾删
	void pop_back()
	{
		erase(--end());
	}

    //头删
	void pop_front()
	{
		erase(begin());
	}

后记

        在list类的实现中,默认成员函数、操作符重载以及增删查改已不再是重点,重点是掌握迭代器的实现,因为与string、vector中的迭代器实现不同,也没有那么简单,上面总结的很清楚,不懂的可以私我或者写在评论区,加油,拜拜!


举报

相关推荐

0 条评论