list的底层与使用

前言:list是带头双向循环链表,物理空间不连续,导致对于迭代器的操作需要进行运算符重载,难点也在于对迭代器的使用与实现

cpp 复制代码
//底层代码
#pragma once
#include<assert.h>
#include<iostream>
using namespace std;
namespace bit
{
	template <typename T>
	struct ListNode
	{
		ListNode<T>* next;
		ListNode<T>* prev;
		T data;
		ListNode(T x = 0)//在这里进行数据的深拷贝
			: next(nullptr), prev(nullptr), data(x)//初始化列表
		{}
	};
	template<typename T,typename ptr , typename ref>
	struct NodeIterator
	{
	
		typedef ListNode<T> Node;
		typedef NodeIterator<T,ptr,ref> iterator;
		
		NodeIterator(Node* tmp)
			: _node( tmp)
		{}
		
		ref operator*() const
		{
			return _node->data;
		}
		//++it
		iterator operator++()
		{
			_node = _node->next;
			return *this;
		}
		// it++
		iterator operator++(int)
		{
			iterator tmp(*this);
			_node = _node->next;
			return tmp;
		}
		//it--
		iterator operator--(int)
		{
			iterator tmp(*this);
			_node = _node->prev;
			return tmp;
		}
		iterator operator--()
		{
			_node = _node->prev;
			return *this;
		}
		bool operator!=(const iterator& n) const
		{
			return _node != n._node;
		}
		ptr operator->()
		{
			return &(_node->data);
		}
		Node* _node;
	};
	template<typename T>
	class list
	{
	public:
		typedef NodeIterator<T,T* , T&> iterator;
		typedef NodeIterator<T,const T* ,const T&> const_iterator;
		typedef ListNode<T> Node;
		list(const list& s)
		{
			size = 0;
			phead = new Node(0);
			phead->prev = phead->next = phead;
			for (auto& e : s)
				push_back(e);

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

		}
		~list()
		{
			clear();
			delete phead;
			size = 0;
			phead = nullptr;
		}
		list()
		{
			size = 0;
			phead = new Node(0);
			phead->prev = phead->next = phead;
			
		}
		void push_back(const T& x)
		{
			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void pop_back()
		{
			erase(end()._node->prev);
		}
		void pop_front()
		{
			erase(begin());
		}
		iterator begin()
		{
			return phead->next;
		}
		iterator end()
		{
			return phead;
		}
		const_iterator begin()const
		{
			return phead->next;
		}
		const_iterator end()const
		{
			return phead;
		}
		void insert(iterator position, const T& val)
		{
			Node* _position = position._node;
			Node* tmp = new Node(val);
			tmp->next = _position;
			tmp->prev = _position->prev;
			_position->prev->next = tmp;
			_position->prev = tmp;
			size++;
		}
		iterator erase(iterator position)
		{
			Node* _position = position._node;
			Node* tt = _position->next;
			Node* tmp = _position->prev;
			tmp->next = _position->next;
			_position->next->prev = tmp;
			delete _position;
			size--;
			_position = nullptr;
			return tt;
		}
        list& operator= ( list x )
        {
            swap(x);
            return *this;
        }

		bool empty()
		{
			return size == 0;
		}
		void swap(list<T> s)
        {
	        std::swap(size, s.size);
	        std::swap(phead, s.phead);
        }
        list& operator= (list<T> x)
        {
	        swap(x);
        	return *this;
        }
	private:
		Node* phead;//头结点
		int size;
		
	};
}

迭代器

迭代器一般需要进行如下操作

std::list<int>::iterator it = s.begin();

while( it != s.end())

{

cout << *it << " ";

it++;

}

也就是需要对运算符 * != ++ 对操作符重载

明确一点:链表中的迭代器是节点的指针 , 指针是内置类型 ,但需要对内置类型的行为重新定义,则可以把内置类型转换成自定义类型(单独为迭代器写一个类)

首先 * 一般是取链表中的元素(节点中的data) ,在迭代器中需要取data,只需要将节点的指针作为迭代器类的成员变量

第二点 迭代器可以分为const 或者是 非const ,则可以将迭代器写成一个类模版

cpp 复制代码
// 首先在begin函数中 分为 iterator begin()    const_iterator begin() const
// typedef NodeIterator< T ,  T& , typename T*>  iterator;
// typedef NodeIterator< T , const T& , T* > const_iterator;
// 就导致NodeIterator只生成两个类
// iterator begin 放回的是iterator(他就只会调用第一个类)
// const_iterator begin返回的是const_iterator(只会调用第二个类)
template<typename T,typename ptr , typename ref>
struct NodeIterator
{

	typedef ListNode<T> Node;
	typedef NodeIterator<T,ptr,ref> iterator;
	
	NodeIterator(Node* tmp)
		: _node( tmp)
	{}
	
	ref operator*() const
	{
		return _node->data;
	}
	//++it
	iterator operator++()
	{
		_node = _node->next;
		return *this;
	}
	// it++
	iterator operator++(int)
	{
		iterator tmp(*this);
		_node = _node->next;
		return tmp;
	}
	//it--
	iterator operator--(int)
	{
		iterator tmp(*this);
		_node = _node->prev;
		return tmp;
	}
	iterator operator--()
	{
		_node = _node->prev;
		return *this;
	}
	bool operator!=(const iterator& n) const
	{
		return _node != n._node;
	}
	ptr operator->()
	{
		return &(_node->data);
	}
	Node* _node;
};

构造函数

方法1:初始化链表初始化

cpp 复制代码
#include<iostream>
#include<vector>
#include<list>
#include<stack>
using namespace std;
int main()
{
	list<int> s = { 1 , 2 , 3 , 4 , 5 };
	for (auto& e : s)cout << e << " ";
	cout << endl;
}

方法2:迭代器区间初始化

cpp 复制代码
#include<iostream>
#include<vector>
#include<list>
#include<stack>
using namespace std;
int main()
{
	vector<int> dp = { 1 , 2 , 3 ,4 , 6 };
	auto it = dp.begin();
	list<int> s(it + 2, dp.end() - 1);
	for (auto& e : s)cout << e << " ";
	cout << endl;
}

方法3:不加任何条件

cpp 复制代码
#include<iostream>
#include<vector>
#include<list>
#include<stack>
using namespace std;
int main()
{
	list<int> s;
    return 0;
}

拷贝构造与赋值重载

均为深拷贝

front与back函数

返回头和尾元素

cpp 复制代码
#include<iostream>
#include<vector>
#include<list>
#include<stack>
using namespace std;
int main()
{
	vector<int> dp = { 1 , 2 , 3 ,4 , 6 };
	int f = dp.front();
	int t = dp.back();
	cout << f << " " << t << endl;

}

insert函数

在某一个位置之前插入一个值或一段区间

cpp 复制代码
#include<iostream>
#include<vector>
#include<list>
#include<stack>
using namespace std;
int main()
{
	list<int> s = { 1 , 2 , 4  , 5 , 6 };
	auto it = s.begin();
	vector<int> dp = { 1 , 0 , 9 };
	s.insert(it, dp.begin(), dp.end());
	for (auto& e : s)cout << e << " ";
	cout << endl;
	s.insert(s.end(), 10);
	for (auto& e : s)cout << e << " ";
	cout << endl;

}

erase函数

去除某一个位置的值,或某一段区间的值

cpp 复制代码
#include<iostream>
#include<vector>
#include<list>
#include<stack>
using namespace std;
int main()
{
	list<int> s = { 1 , 2 , 3 , 5 , 3 };
	s.erase(s.begin());
	list<int> p = { 4 , 5 , 12 , 3 , 4 };
	s.erase(++p.begin(), --p.end());
	for (auto& e : p)cout << e << " ";
	cout << endl;

}

push_back 与push_front函数

本质复用insert函数 尾插 头插

pop_back与pop_front函数

本质复用erase函数

assign函数

将链表中的内容重新分配

cpp 复制代码
#include<iostream>
#include<vector>
#include<list>
#include<stack>
using namespace std;
int main()
{
	list<int> s = { 1, 2, 3, 4, 6 };
	list<int> k = { 9 , 1 , 0 };
	s.assign({ 2,3,45 });
	for (auto& e : s)cout << e << " ";
	cout << endl;
	s.assign(k.begin(),k.end());
	for (auto& e : s)cout << e << " ";
	cout << endl;

}
相关推荐
hairenjing112316 分钟前
使用 Mac 数据恢复从 iPhoto 图库中恢复照片
windows·stm32·嵌入式硬件·macos·word
九鼎科技-Leo2 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
躺不平的理查德4 小时前
数据结构-链表【chapter1】【c语言版】
c语言·开发语言·数据结构·链表·visual studio
阿洵Rain4 小时前
【C++】哈希
数据结构·c++·算法·list·哈希算法
Leo.yuan4 小时前
39页PDF | 华为数据架构建设交流材料(限免下载)
数据结构·华为
九鼎科技-Leo5 小时前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net
半夜不咋不困5 小时前
单链表OJ题(3):合并两个有序链表、链表分割、链表的回文结构
数据结构·链表
忘梓.5 小时前
排序的秘密(1)——排序简介以及插入排序
数据结构·c++·算法·排序算法
y_m_h8 小时前
leetcode912.排序数组的题解
数据结构·算法
1 9 J8 小时前
数据结构 C/C++(实验三:队列)
c语言·数据结构·c++·算法