【C++】双向线性链表容器的实现

// 双向线性链表容器
#include <cstring>
#include <iostream>
#include <stdexcept>
using namespace std;

// 链表类模板
template<typename T>
class List
{
public:
	// 构造、析构、支持深拷贝的拷贝构造和拷贝赋值
	List(void) : m_head(NULL), m_tail(NULL) {}
	~List(void)
	{
		clear();
	}

	List(List const& that) : m_head(NULL), m_tail(NULL)
	{
		for (Node* node = that.m_head; node != NULL;
					node = node->m_next)
			push_back(node->m_data);
	}

	List& operator = (List const& rhs)
	{
		if (&rhs != this)
		{
			List list(rhs);
			swap(m_head, list.m_head);
			swap(m_tail, list.m_tail);
		}
		return *this;
	}

	// 获取首元素
	T& front(void)
	{
		if (empty())
			throw underflow_error("链表下溢!");	//下溢异常
		return m_head->m_data;
	}

	T const& front(void) const
	{
		return const_cast<List*>(this)->front();
	}

	// 向首部压入
	void push_front(T const& data)
	{
		m_head = new Node(data, NULL, m_head);
		if (m_head->m_next != NULL)
			m_head->m_next->m_prev = m_head;
		else
			m_tail = m_head;
	}

	// 从首部弹出
	void pop_front(void)
	{
		if (empty())
			throw underflow_error("链表下溢!");
		Node* next = m_head->m_next;
		delete m_head;
		m_head = next;
		if (m_head)
			m_head->m_prev = NULL;
		else
			m_tail = NULL;
	}

	// 获取尾元素
	T& back(void)
	{
		if (empty())
			throw underflow_error("链表下溢!");
		return m_tail->m_data;
	}

	T const& back(void) const
	{
		return const_cast<List*>(this)->back();
	}

	// 向尾部压入
	void push_back(T const& data)
	{
		m_tail = new Node(data, m_tail);
		if (m_tail->m_prev != NULL)
			m_tail->m_prev->m_next = m_tail;
		else
			m_head = m_tail;
	}

	// 从尾部弹出
	void pop_back(void)
	{
		if (empty())
			throw underflow_error("链表下溢!");
		Node* prev = m_tail->m_prev;
		delete m_tail;
		m_tail = prev;
		if (m_tail != NULL)
			m_tail->m_next = NULL;
		else
			m_head = NULL;
	}

	// 删除所有匹配元素
	void remove(T const& data)
	{
		for (Node* node = m_head, *next; node != NULL;
				node = next)
		{
			next = node->m_next;
			if (equal(data, node->m_data))
			{
				if (node->m_prev != NULL)
					node->m_prev->m_next = node->m_next;
				else
					m_head = node->m_next;

				if (node->m_next)
					node->m_next->m_prev = node->m_prev;
				else
					m_tail = node->m_prev;

				delete node;
			}
		}
	}

	// 清空
	void clear(void)
	{
		while (!empty())
			pop_back();
	}

	// 判空
	bool empty(void) const
	{
		return NULL == m_head &&  NULL == m_tail;
	}

	// 大小
	size_t size(void) const
	{
		size_t nodes = 0;
		for (Node* node = m_head; node != NULL;
				node = node->m_next)
			++nodes;
		return nodes;
	}

	// 下标运算符————SHIT!
	T& operator[] (size_t i)
	{
		for (Node* node = m_head; node != NULL;
				node = node->m_next)
			if (0 == i--)
				return node->m_data;
		throw out_of_range("下标越界!");
	}

	T const& operator[] (size_t i) const
	{
		return const_cast<List&>(*this)[i];
	}

	// 插入输出流
	friend ostream& operator << (ostream& os,
				List const& list)
	{
		for (Node* node = list.m_head; node != NULL;
				node = node->m_next)
			os << *node;
		return os;
	}

private:
	// 节点类模板
	class Node
	{
	public:
		Node(T const& data, Node* prev = NULL, Node* next = NULL)
			: m_data(data), m_prev(prev), m_next(next) {}
		friend ostream& operator << (ostream& os, Node const& node)
		{
			return os << '[' << node.m_data << ']';
		}
		T	  m_data;	// 数据
		Node* m_prev;	// 前指针
		Node* m_next;	// 后指针
	};

	// 判断元素是否相等
	bool equal(T const & a, T const& b) const
	{
		return a == b;
	}

	Node* m_head;	// 头指针
	Node* m_tail;	// 尾指针
public:
	// 正向迭代器
	class Iterator
	{
	public:
		Iterator(Node* head = NULL, Node* tail = NULL, Node* node = NULL)
			: m_head(head), m_tail(tail), m_node(node) {}
		bool operator == (Iterator const& rhs) const
		{
			return m_node == rhs.m_node;
		}

		bool operator != (Iterator const& rhs) const
		{
			return ! (*this == rhs);
		}

		Iterator& operator++ (void)
		{
			if (m_node)
				m_node = m_node->m_next;
			else
				m_node = m_head;
			return *this;
		}

		Iterator const operator++ (int)
		{
			Iterator old = *this;
			++*this;
			return old;
		}

		Iterator& operator-- (void)
		{
			if (m_node)
				m_node = m_node->m_prev;
			else
				m_node = m_tail;
			return *this;
		}

		Iterator const operator-- (int)
		{
			Iterator old = *this;
			--*this;
			return old;
		}

		T& operator* (void) const
		{
			return m_node->m_data;
		}

		T* operator->(void) const
		{
			return &**this;
		}
	private:
		Node* m_head;
		Node* m_tail;
		Node* m_node;
		friend class List;
	};

	// 获取起始正向迭代器————指向第一个元素
	Iterator begin(void)
	{
		return Iterator(m_head, m_tail, m_head);
	}

	// 获取终止正向迭代器————指向最后一个元素的下一个位置
	Iterator end(void)
	{
		return Iterator(m_head, m_tail);
	}

	// 在正向迭代器前插入,返回指向新插入元素的迭代器
	Iterator insert(Iterator loc, T const& data)
	{
		if (loc == end())
		{
			push_back(data);
			return Iterator(m_head, m_tail, m_tail);
		}
		else
		{
			Node* node = new Node(data, loc.m_node->m_prev, loc.m_node);
			if (node->m_prev)
				node->m_prev->m_next = node;
			else
				m_head = node;
			node->m_next->m_prev = node;
			return Iterator(m_head, m_tail, node);
		}
	}

	// 删除迭代器所指向的元素,并返回该元素之后的迭代器
	Iterator erase(Iterator loc)
	{
		if (loc == end())
			throw invalid_argument("无效参数!");
		if (loc.m_node->m_prev)
			loc.m_node->m_prev->m_next = loc.m_node->m_next;
		else
			m_head = loc.m_node->m_next;
		if (loc.m_node->m_next)
			loc.m_node->m_next->m_prev = loc.m_node->m_prev;
		else
			m_tail = loc.m_node->m_prev;
		Node* next = loc.m_node->m_next;
		delete loc.m_node;
		return Iterator(m_head, m_tail, next);
	}

	// 常正向迭代器
	// 反向迭代器
	// 常反向迭代器

};

// 针对char const* 类型的成员特化版本
template<>
bool List<char const*>::equal(char const* const& a, char const* const& b) const
{
	return (0 == strcmp(a, b));
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-29 19:05:31

【C++】双向线性链表容器的实现的相关文章

线性链表其他种类(静态,双向,循环)的存储结构和常见操作

一.静态单链表 在不支持动态空间分配的环境中,要使用链表存储数据,那么可采用静态链表的方法:即在一块预分配的存贮空间中,用下标作为指针链来构成链式结构. //既然是静态链表,那么可以使用一维数组实现存储,java没有指针,那么就用这来使用链表结构 //在不支持动态空间分配的环境中,要使用链式结构技术,可采用静态链表的方法:即在一块预分配的存贮空间中,用下标作为指针. //存储结构:在数组中增加一个“指针”域,存放下一元素在数组中的下标.且0为代表空指针 //设S为SLinkList型变量,若第i

线性链表的双向链表——java实现

.线性表链式存储结构:将采用一组地址的任意的存储单元存放线性表中的数据元素. 链表又可分为: 单链表:每个节点只保留一个引用,该引用指向当前节点的下一个节点,没有引用指向头结点,尾节点的next引用为null. 循环链表:一种首尾相连的链表. 双向链表:每个节点有两个引用,一个指向当前节点的上一个节点,另外一个指向当前节点的下一个节点. 下面给出线性表双向链表的实现:java中LinkedList是线性表的链式实现,是一个双向链表. import java.util.NoSuchElementE

数据结构C语言实现——线性链表

declaration.h #ifndef DECLARATION_H_INCLUDED #define DECLARATION_H_INCLUDED #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define OVERFLOW -2 #define ElemType int typedef ElemType* Triplet; typedef int Status; type

数据结构 线性链表

//线性链表头文件 #ifndef _vzhanglinklist #define _vzhanglinklist typedef struct _LinkNode{ struct _LinkNode *pnext; }LinkNode; typedef void LinkList; //创建线性链表 _declspec(dllexport) LinkList* LinkList_Create(); //销毁线性链表 _declspec(dllexport) int LinkList_Destr

从新定义线性链表及其基本操作

链表在空间的合理利用上和插入.删除时不需要移动等优点,因此在很多场合下,它是线性表的首先储存结构.然而它也存在着实现某些基本操作,如求线性表的长度时不如顺序储存结构的特点.因而从新定义线性链表及其基本操作 头文件: #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define INFEASIBLE -1 #define MYOVERFLOW -2 typedef int Status; typedef int Elemty

线性链表之顺序表

顺序表中数据元素的存储地址是其序号的线性函数,只要确定了存储顺序表的起始地址(即 基地址),计算任意一个元素的存储地址的时间是相等的,具有这一特点的存储结构称为[随机存储]. 使用的基本数据结构:数组 特点:顺序存取,随机访问. /* Name: SeqList Copyright: 1.0 Author: Johnny Zen Date: 04/06/17 21:51 Description: 线性链表之顺序表 *//* Chinese:顺序(线性)表 English:SeqList*/#in

利用线性链表基本操作完成两个有序线性表的合并

La.Lb线性链表升序排列,将结果放在Lc链表里.之前有文章写过两个有序链表的合并 区别在于,前面的做法是保留La的头节点,free掉Lb的头节点,将余下节点串起来.这种方法是面向过程编程 而现在讨论的做法,是单独建立一个Lc链表,利用一些已经写好的基本操作函数来完成,这种模块化编程做法实际上还简单些.不光模块函数里写不了几行,在调用这些函数时减少了不必要的琐碎过程的思考时间. 该做法的核心思想:将每轮比较过后偏小的那个节点从相应链表中删除(这是头节点的指针不会指向该节点了,但该节点的空间依旧保

5-6-广义表(扩展线性链表存储表示)-数组和广义表-第5章-《数据结构》课本源码-严蔚敏吴伟民版

课本源码部分 第5章  数组和广义表 - 广义表(扩展线性链表存储表示) ——<数据结构>-严蔚敏.吴伟民版        源码使用说明  链接??? <数据结构-C语言版>(严蔚敏,吴伟民版)课本源码+习题集解析使用说明        课本源码合辑  链接??? <数据结构>课本源码合辑        习题集全解析  链接??? <数据结构题集>习题解析合辑        本源码引入的文件  链接? Status.h.SequenceString.c  

创建单线性链表的不同表示方法和操作

创建单线性链表,常见的有头插法.尾插法创建线性链表,常见的操作有:创建链表.查找.删除.添加元素.求逆链等操作. 这里首先用头插法创建链表: //头指针唯一确定一个单链表 #define MaxSize 15 typedef int elem_type ; typedef struct linklist { elem_type data; struct linklist *next; } Linklist; //头插入法建立链表:每次将头结点的指针赋值给新结点,然后将新节点赋值给头结点指针 Li