数据结构—链表及其迭代器的封装

链表简介

链表是采用链式存储方式实现的一种逻辑上类似于数组的数据结构,其元素之间的逻辑顺序是连续的,但在内存中的物理存储顺序是不连续的,因此不能使用下标的方式读取元素。为了实现数据元素的各种操作,我们把各元素封装成一个结构体——节点(下文都使用此称谓),每个节点内包括一个存储数据的数据成员m_nValue和一个指向该节点的指针m_pNext。

代码实现

节点定义

m_nValue用于存储数据,m_pNext用于指向下一节点,pLinkList为指向ListNode的指针。

typedef struct ListNode{
	int m_nValue;
	struct ListNode *m_pNext;
}*pLinklist;

封装链表类

——链表类声明

class  LinkList
{
public:											/*************构造函数************/
	 LinkList();								//构造空链表
	 LinkList(unsigned int count, int value);	//构造具有count个值为value的链表
	 LinkList(const LinkList& L);				//以另一个对象L构造
	~ LinkList(){};								//析构函数,空函数

private:										/*************数据成员************/
	pLinklist pHead;							//指向头节点指针
	pLinklist pEnd;								//指向尾节点指针
	unsigned int _size;							//链表长度

public:											/*************公有接口************/
	bool Empty() const;							//检查链表是否为空,空返回true,否则返回false
	unsigned int size() const;					//返回表长度
	void ClearList();							//清空链表
	void push_back(int value);					//将value插入到表尾
	void pop_back();							//删除尾节点
	int back() const;							//返回尾节点值
	int front() const;							//返回头节点值
	int GetElem(int i) const;					//返回第i个位置的值,若i大于size返回0
	void InsertElem(int i,int a);				//将a插入到第i个位置
	pLinklist Begin() const;					//返回头节点位置
	pLinklist End() const;						//返回尾节点的下一个位置
	LinkList& operator=(const LinkList& L);		//赋值构造函数
};

——链表类各操作定义

构造函数:包括默认构造函数,接受两个参数的构造函数和copy构造函数

LinkList::LinkList() //默认构造函数,构造空表
{
	pHead = nullptr;
	pEnd = nullptr;
	_size = 0;
}

LinkList::LinkList(unsigned int count, int value)  //构造具有count个值为value的链表
{
	if (count <= 0)	//若count<=0,直接返回
		return;
	_size = 0;
	pHead = (pLinklist)new ListNode;
	pHead->m_nValue = value;
	pHead->m_pNext = nullptr;
	_size = count;
	pLinklist p = pHead;
	pLinklist r = nullptr;
	for (int k = 0; k < _size-1; k++)
	{
		r = (pLinklist)new ListNode;
		r->m_nValue = value;
		r->m_pNext = nullptr;
		p->m_pNext = r;
		p = p->m_pNext;
	}
	pEnd = p;
}

LinkList::LinkList(const LinkList& L)  //以另一个链表构造
{
	if (L._size == 0)	//若L为空表,执行默认构造函数
	{
		LinkList();
		return;
	}
	_size = L._size;
	pLinklist p, r;
	p = L.pHead;
	pHead = (pLinklist)new ListNode;
	pHead->m_nValue = p->m_nValue;
	pHead->m_pNext = nullptr;
	r = pHead;
	for (int i = 0; i < _size - 1; i++)
	{
		p = p->m_pNext;
		pLinklist temp = (pLinklist)new ListNode;
		temp->m_nValue = p->m_nValue;
		temp->m_pNext = nullptr;
		r->m_pNext = temp;
		r = r->m_pNext;
	}
	pEnd = r;
}

Empty(),判断链表是否为空,空返回true,非空返回false。

bool LinkList::Empty() const
{
	return (pHead == nullptr) ? true : false;
}

size(),返回链表长度。

unsigned int LinkList::size() const
{
	return _size;
}

ClearList(),清空链表。

void LinkList::ClearList()
{
	if (_size == 0)
		return;
	pLinklist r;
	while (pHead!=pEnd)
	{
		r = pHead;
		pHead = pHead->m_pNext;
		delete r;
		_size--;
	}
	r = pHead;
	pHead = pEnd = nullptr;
	delete r;
	_size--;
}

push_back(int value),往表尾添加一个节点,节点值为value。

void LinkList::push_back(int value)
{
	pLinklist p = (pLinklist)new ListNode;
	p->m_nValue = value;
	p->m_pNext = nullptr;
	pEnd->m_pNext = p;		//将p添加到当前尾节点指向的下一节点
	pEnd = pEnd->m_pNext;	//将尾指针指向p
	_size++;
}

pop_back(),删除尾节点。

void LinkList::pop_back()					//删除尾节点
{
	pLinklist p = pHead;
	while (p->m_pNext!=pEnd)
	{
		p = p->m_pNext;
	}
	pEnd = p;					//将尾指针前移一个节点
	p = p->m_pNext;				//找到尾节点
	delete p;					//删除尾节点
	pEnd->m_pNext = nullptr;	//尾节点指针指向空
	_size--;
}

front(),back(),返回头、尾节点值。

int LinkList::back() const		//返回尾节点值
{
	if (!pEnd)
	{
		return 0;
	}
	return pEnd->m_nValue;
}

int LinkList::front() const		//返回头节点值
{
	if (!pHead)
	{
		return 0;
	}
	return pHead->m_nValue;
}

GetELem(int i),返回某个位置的值,若不存在则返回0。

int LinkList::GetElem(int i) const	//返回第i个位置的值,若i大于size返回0
{
	if (!pHead || i > _size)		//若链表为空或i大于链表长度,返回0
	{
		return 0;
	}
	int count = 1;
	pLinklist p = pHead;
	while (count<i)
	{
		p = p->m_pNext;
		count++;
	}
	return p->m_nValue;
}

InsertELem(int i,int a),将a插入到第i位置,若不存在第i位置,则不插入。

void LinkList::InsertElem(int i, int a)		//将a插入到第i个位置
{
	if (!pHead || i > _size || i <= 0)		//若不存在第i位置,则不插入
	{
		return;
	}
	if (i == 1)
	{
		pLinklist r = (pLinklist)new ListNode;
		r->m_nValue = a;
		r->m_pNext = pHead;
		pHead = r;
		_size++;
		return;
	}
	int count = 1;
	pLinklist p = pHead;
	while (count < i - 1)
	{
		p = p->m_pNext;
		count++;
	}
	pLinklist r = (pLinklist)new ListNode;
	r->m_nValue = a;
	r->m_pNext = p->m_pNext;
	p->m_pNext = r;
	_size++;
}

重载运算符‘=’,接受一个LinkList的引用,将‘=’右值L赋值给‘=’左值。注意返回值为LinkLis的引用,即返回this自身。

LinkList& LinkList::operator=(const LinkList& L)
{
	if (&L == this)		//若L为自身,直接返回
	{
		return *this;
	}
	if (L._size == 0)	//若L为空,调用默认构造函数
	{
		LinkList();
		return *this;
	}
	_size = L._size;
	pLinklist p, r;
	p = L.pHead;
	pHead = (pLinklist)new ListNode;
	pHead->m_nValue = p->m_nValue;
	pHead->m_pNext = nullptr;
	r = pHead;
	for (int i = 0; i < _size - 1; i++)
	{
		p = p->m_pNext;
		pLinklist temp = (pLinklist)new ListNode;
		temp->m_nValue = p->m_nValue;
		temp->m_pNext = nullptr;
		r->m_pNext = temp;
		r = r->m_pNext;
	}
	pEnd = r;
	return *this;
}

封装迭代器类

——迭代器类声明

具有一个私有数据成员,指向节点的指针p。

class LinkList_iterator{
private:
	pLinklist _index;										//指向链表节点指针
public:
	LinkList_iterator();									//默认构造函数,指向空
	LinkList_iterator(const pLinklist p);					//以另一迭代器构造
	~LinkList_iterator(){};									//析构函数,空函数
public:
	bool operator==(const LinkList_iterator& rhs) const;	//判断this和rhs是否指向同一节点,是返回true,否返回false
	bool operator!=(const LinkList_iterator& rhs) const;	//判断this和rhs是否指向同一节点,否返回true,是返回false
	int operator*() const;									//提领该迭代器指向的值,即this->_index->m_nValue
	LinkList_iterator& operator++();						//前置++,使迭代器指向下一节点
	LinkList_iterator operator++(int);						//后置++,使迭代器指向下一节点
};

为了使迭代器可以指向链表的节点并进行遍历,在链表类中添加如下代码。将迭代器类声明为链表类的friend。并为链表类添加两个函数begin()和end(),返回指向链表头节点和尾节点下一位置的迭代器。

class  LinkList
{
~
~
~
friend class LinkList_iterator;				//将LinkList_iterator声明为friend
public:
	typedef LinkList_iterator iterator;

	LinkList_iterator begin() const			//返回指向链表头节点的迭代器
	{
		return LinkList_iterator(pHead);
	}

	LinkList_iterator end() const			//返回指向链表尾节点下一位置的迭代器
	{
		return LinkList_iterator();
	}
~
~
~
}

——迭代器类定义

两个构造函数:默认构造函数LinkList_iterator(),该迭代器指向空;LinkList_iterator(const pLinkList p),与指针p指向同一节点。

LinkList_iterator::LinkList_iterator()
{
	_index = nullptr;
}

LinkList_iterator::LinkList_iterator(const pLinklist p)
{
	_index = p;
}

重载运算符‘==’,判断运算符左值和右值是否指向同一节点,若指向同一节点返回true,否则返回false;重载运算符‘!=’,作用相同返回值相反。

bool LinkList_iterator::operator==(const LinkList_iterator& rhs) const
{
	return _index == rhs._index;
}

bool LinkList_iterator::operator!=(const LinkList_iterator& rhs) const
{
	return _index != rhs._index;
}

重载‘++’运算符,使迭代器指向下一节点,分为前置和后置。

LinkList_iterator& LinkList_iterator::operator++()	//前置
{
	_index = _index->m_pNext;
	return *this;
}
LinkList_iterator LinkList_iterator::operator++(int)	//后置
{
	LinkList_iterator temp = *this;
	_index = _index->m_pNext;
	return temp;
}

重载运算符‘*’,提领迭代器指向的节点值。

int LinkList_iterator::operator*() const
{
	return _index->m_nValue;
}

测试代码

int main()
{
	LinkList L(3, 3);
	LinkList L2;
	L2 = L;
	L2.push_back(4);
	L2.push_back(5);
	L2.pop_back();
	L2.InsertElem(1, 7);
	//L2.ClearList();
	if (!L2.Empty())
	{
		cout << "链表长度:" << L2.size() << "\n" << "元素:";
		LinkList::iterator iter = L2.begin();
		while (iter != L2.end())
		{
			cout << *iter << " ";
			iter++;
		}
		cout << "\n" <<"首元素:"<< L2.front() << " "<<"尾元素:" << L2.back();
	}
	getchar();
}

输出

链表长度:5
元素:7 3 3 3 4
首元素:7 尾元素:4

总结

链表读取任一元素都需要从头遍历,因此读取效率为O(n),低于数组的O(1),但在表内任一位置添加和删除元素时,不需要挪动其他元素位置,因此适合于经常添加和删除元素的场合,并且链表的长度是动态增加的,其物理存储位置不连续,因此空间利用率很高。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值