双循环链表(C++)

    在C++里,存在多种类型的表,其中有一种线性表,链表则是一种线性表,正如它的名字一样,链表的样子就像是用一条链子串起来的表(这里,我主要讲的是双循环链表)

而用来连接链子的每个环节的是指针,在最基本的单向不循环链表中,有一根单向指针next,这根指针的作用显而易见,就是串联上下两个节点。

    说到串联节点,那么必不可少的自然是节点,节点又如何创建呢。首先,我们要创建一个节点类(class Node),在双循环链表中,想要实现双循环,那么就要求要有双向效果,也就是用来指向的指针应该要有两根,一根指向本节点的下一个节点(next),一根指向本节点的上一个节点(prior),以此达到双向的效果

这里,我以一个记录学生信息的双循环链表作为例子

class Node
{
public:
	int ID;
	string name;
	Node * next;
	Node * prior;
	Node() :next(nullptr), prior(nullptr) {}
	Node(int ID, string name) :ID(ID), name(name), next(nullptr), prior(nullptr) {}
	Node(Node &n) :ID(n.ID), name(n.name), next(nullptr), prior(nullptr) {}
	void display()
	{
		cout << "ID:" << ID << endl << "Name:" << name << endl;
	}
};

 在这个类里面,要有三种构造函数,无参构造主要是为了在链表类里面创建头结点使用,第一类有参构造是为了主函数内创建节点,而第二类构造则是拷贝构造,目的在于拷贝节点信息。

    创建完节点,需要做的便是将节点串联起来。串联节点,首先需要有一个起点,这个起点就是一个标志性作用,在循环链表里面,头结点既是开头也是结尾,起到关键作用

所以,在创建链表的时候,首先应该先有一个Node型的头结点

Link::Link()
{	
		head = new Node();
		head->name = "头结点";
		head->next = head;
		head->prior = head;
}

 在没有插入任何其他节点的时候,头结点既是头节点的上一节点也是头结点的下一节点,也就是所有指针全部指向自己。当然,这个头结点是private的,不可被外界访问到。有了头节点之后,我们可以开始对这个链表进行各种操作了。

class Link
{
private:
	Node * head;
public:
	Link();
	~Link();
	void traverse1();
	void traverse2();
	void insert(Node &n);
	void deleteByID(int id);
	void modifyByID(int id, string name);
};

这里,我只写了顺序遍历、倒序遍历、插入、按ID关键字删除、 按关键字修改信息五个函数。首先,我们先讲插入,没有插入就没有其他节点的存在。双循环链表的插入比较复杂一点,最好可以用纸笔进行模拟,可以促进理解。

    插入,有两种情况,第一种是在除了头结点外为空的链上插入,此时我们要做的是将头节点的next指向要插入的节点(下面称为本节点),将头结点的prior指向本节点,将本节点的prior指向头结点,将本节点的next指向头结点。这样就可以实现头结点与本节点之间的双向循环。至于第二种,就是在链表已经足够长的情况下插入节点,我们这里的插入只是单纯插在尾部,至于其他类型的插入,大致不会差多少,所以谨以此例子作为示范。要插在尾部,我们要先找到尾部在哪里,于是有了一根指针(tail),tail=head,当tail的下一个节点不是头结点的时候,我们就继续找下一个,一直到他指向头结点,此时tail所代表的节点必然就是最后一个节点,new 出一个新的实体,这个实体的所有信息与传入的节点的信息完全一致,也就是进行拷贝,然后使newNode得next指向头结点,将头结点的prior指向newNode,将newNode的prior指向tail(最后一个节点),将最后一个节点的next指向newNode,这样,newNode就成为新的最后一个节点,并且是支持双向循环的。

void Link::insert(Node &n)
{
	Node *tail = head;
	if (head->next == head)
	{
		Node *newNode = new Node(n);
		head->next = newNode;
		head->prior = newNode;
		newNode->prior = head;
		newNode->next = head;
	}
	else
	{
		while (tail->next != head)
		{
			tail = tail->next;
		}
		Node *newNode = new Node(n);
		newNode->next = head;
		head->prior = newNode;
		newNode->prior = tail;
		tail->next = newNode;
	}
}

    然后我们讲遍历,两种遍历的方法基本一致,只是顺序不一样而已,我只讲一种顺序遍历。首先创建一根副指针p,p指向头节点的下一个节点,然后将其输出出来,然后使p=p->next,实现指向再下一个节点,以循环不断做,直到p指向的是头结点才结束,这样就能够全部打印一遍。

void Link::traverse1()
{
	Node *p = head->next;
	while (p != head)
	{
		p->display();
		p = p->next;
	}
	cout << endl;
}

    然后是删除,按关键字删除,那么首先要做的就是找到关键字所在地方,建造两根副指针,p,q;p指向头结点的下一个节点,q指向本节点,以p查看每个节点信息,q作为他后面的节点,方便q的修改,当找到关键字后,先修改q的next,使其变成p的next,然后删除p节点,这样p所在位置的节点就被删除了,然后还需要做的是将原来p的next的节点的prior指向q,所以我们让p指向该节点,然后使他的prior指向q.这样就完成删除。(查找过程依然采用遍历查找)

void Link::deleteByID(int id)
{
	Node *p = head->next;
	Node *q = head;
	while (p != head)
	{
		if (p->ID == id)
		{
			q->next = p->next;
			delete(p);
			p = q->next;
			p->prior = q;
		}
		else
		{
			p = p->next;
			q = q->next;
		}
	}
}

     最后讲的是按关键字修改。当然了,按关键字修改最重要的依然是找到关键字,所以重复同样的方法,创建副指针,循环查找,当找到关键字的时候,将信息修改成传入的信息即可。

void Link::modifyByID(int id, string name)
{
	Node *p = head->next;
	Node *q = head->prior;
	while (p != head)
	{
		if (p->ID == id)
		{
			p->name = name;
		}
		p = p->next;
		q = q->prior;
	}
}

最后补充说一下,查找的过程中可能遍历了整个链都找不到关键字,此时应该要报错或者有其他处理方法,我这里写的都是认为一定可以找到的,所以没有进行处理,有需要的还是要做一下处理。

     那么双循环链表就讲到这里,如果有不同意见或者见解,又或者觉得我有哪里写错了,欢迎私聊我,大家一起学习一起进步,谢谢啦(QQ:2245440426)

 

完整代码如下

#include<iostream>
#include<string>
using namespace std;
/*######################################*/
/*            学生类双链表              */
/*######################################*/
class Node
{
public:
	int ID;
	string name;
	Node * next;
	Node * prior;
	Node() :next(nullptr), prior(nullptr) {}
	Node(int ID, string name) :ID(ID), name(name), next(nullptr), prior(nullptr) {}
	Node(Node &n) :ID(n.ID), name(n.name), next(nullptr), prior(nullptr) {}
	void display()
	{
		cout << "ID:" << ID << endl << "Name:" << name << endl;
	}
};
class Link
{
private:
	Node * head;
public:
	Link();
	~Link();
	void traverse1();
	void traverse2();
	void insert(Node &n);
	void deleteByID(int id);
	void modifyByID(int id, string name);
};
Link::Link()
{	
		head = new Node();
		head->name = "头结点";
		head->next = head;
		head->prior = head;
}
Link::~Link()
{
		while (head != nullptr)
		{
			Node *p = head->next;
			delete(head);
			head = p;
		}
}
void Link::traverse1()
{
	Node *p = head->next;
	while (p != head)
	{
		p->display();
		p = p->next;
	}
	cout << endl;
}
void Link::traverse2()
{
	Node *p = head->prior;
	while (p != head)
	{
		p->display();
		p = p->prior;
	}
	cout << endl;
}
void Link::insert(Node &n)
{
	Node *tail = head;
	if (head->next == head)
	{
		Node *newNode = new Node(n);
		head->next = newNode;
		head->prior = newNode;
		newNode->prior = head;
		newNode->next = head;
	}
	else
	{
		while (tail->next != head)
		{
			tail = tail->next;
		}
		Node *newNode = new Node(n);
		newNode->next = head;
		head->prior = newNode;
		newNode->prior = tail;
		tail->next = newNode;
	}
}
void Link::deleteByID(int id)
{
	Node *p = head->next;
	Node *q = head;
	while (p != head)
	{
		if (p->ID == id)
		{
			q->next = p->next;
			delete(p);
			p = q->next;
			p->prior = q;
		}
		else
		{
			p = p->next;
			q = q->next;
		}
	}
}
void Link::modifyByID(int id, string name)
{
	Node *p = head->next;
	Node *q = head->prior;
	while (p != head)
	{
		if (p->ID == id)
		{
			p->name = name;
		}
		p = p->next;
		q = q->prior;
	}
}
int main()
{
	Link link;
	Node s1(1001, "张三");
	Node s2(1002, "李四");
	Node s3(2001, "王五");
	link.insert(s1);
	link.insert(s2);
	link.insert(s3);
	link.traverse1();
	link.traverse2();
	link.deleteByID(s2.ID);
	link.traverse1();
	link.traverse2();
	link.modifyByID(s3.ID, "赵六");
	link.traverse1();
	link.traverse2();
	return 0;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值