C++实现线性表 - 03 双向循环链表

数据结构与算法专栏 —— C++实现

写在前面:

上一讲我们学会了如何创建一个单链表,这一讲我们来看看双向循环链表是如何进行操作的,我相信经过上面的学习,这一讲对你来说不会太吃力~

什么是双向链表 

C++实现线性表 - 03 双向循环链表_第1张图片

正如上图所示,双向链表就只是在单向链表的基础上,增加了一个指向上一个结点的指针,操作上就只用多考虑一个指针罢了。而双向循环链表就是在双向链表的基础上将头尾结点也连接起来,如下图所示。

C++实现线性表 - 03 双向循环链表_第2张图片

另外要注意的是,我们这里的头指针和尾指针指向的结点不存放数据,这样可能会更易于初学者去理解(我当初就是这样的doge),当然你也可以不用尾指针,因为头结点的指向上一个结点的指针就是最后一个结点,这里全凭个人喜好啦~

链表的创建

这里的结点的结构体就增加了一个front指针。

struct Node {
	int val;		//数据
	Node* front;	//指向上一个结点的指针
	Node* next;		//指向下一个结点的指针
};

我们这里初始化的时候顺带创建一个存储数据的新结点,这样我们的尾结点的front在头插法的时候也能一直指向最后一个结点啦。

void initnode(int key) {
	Node* new_node = new Node;
	new_node->val = key;
	head = new Node;
	last = new Node;
	//将新结点连接到头结点和尾结点当中
	head->next = new_node;
	last->front = new_node;
	//将头尾结点相连
	head->front = last;
	last->next = head;
	head->val = last->val = -1;	//将两个结点赋一个不可能的值
}

头插法

其实双向链表的插入操作相对于单向链表而言会更加方便,因为想要插入一个结点到链表中的话不用找到删除结点的上一个结点了,直接调用删除结点的front就可以找到,我们这里还是给大家讲解头插法和尾插法的做法。

C++实现线性表 - 03 双向循环链表_第3张图片C++实现线性表 - 03 双向循环链表_第4张图片

C++实现线性表 - 03 双向循环链表_第5张图片

 其实做法和单向链表差不了多少,直接来看代码。

void head_insert(int key) {
	Node* new_node = new Node;
	new_node->val = key;
	new_node->next = head->next;	//将新结点的next指向头结点的next
	new_node->front = head;			//将新结点的front指向头结点
	head->next->front = new_node;	//将头结点的下一个结点的front指向新结点
	head->next = new_node;			//将头结点的next指向新结点
}

尾插法

尾插法和头插法几乎没什么差别,同样直接来看代码。

void end_insert(int key) {
	Node* new_node = new Node;
	new_node->val = key;
	new_node->next = last;				//将新结点的next指向尾结点
	new_node->front = last->front;		//将新结点的front指向尾结点的上一个结点
	new_node->front->next = new_node;	//将新结点的上一个结点的下一个结点指向新结点
	last->front = new_node;				//将尾结点的front指向新结点
}

删除结点

删除结点的操作和单向链表几乎一样,而且上面提到过,咱不需要另外创建一个临时指针寻找删除结点的上一个结点,直接调用删除结点的front即可,不过这里方便大家理解,我们创建三个临时指针,分别指向删除结点的上一个结点、删除结点和删除结点的下一个结点。

void delete_node(int key) {
	Node* temp = head->next;
	while (temp->val != key){
		//判断是否有该结点
		if (temp->val == -1){
			cout << "没有此结点" << endl;
			return;
		}
		temp = temp->next;
	}

	Node* Prev = temp->front;	//创建临时结点指向删除结点的上一个结点
	Node* Next = temp->next;	//创建临时结点指向删除结点的下一个结点
	Prev->next = Next;			//删除结点的上一个结点的next指向删除结点的下一个结点
	Next->front = Prev;			//删除结点的下一个节点的front指向删除结点的上一个结点
	/*
	也可以不用创建Prev和Next指针
	temp->front->next = temp->next;
	temp->next->front = temp->front;
	*/

	temp->front = temp->next = NULL;
	delete[]temp;
	temp = NULL;
}

遍历链表

双向链表的遍历和我们的单向链表的遍历是几乎一样的,直接看代码!

void show()
{
	Node* temp = head->next;  //创建一个临时指针
	if (temp->val == -1)
	{
		cout << "当前还没有创建结点" << endl;
		return;
	}
	while (temp->val != -1)      //遍历整个链表
	{
		cout << temp->val << " ";
		temp = temp->next;
	}
	cout << endl;
}

全部代码

#include 
using namespace std;

struct Node {
	int val;		//数据
	Node* front;	//指向上一个结点的指针
	Node* next;		//指向下一个结点的指针
};

Node* head;		//头指针,设为全局变量方便使用
Node* last;		//尾指针

void initnode(int);
void head_insert(int);
void end_insert(int);
void delete_node(int);
void show();

int main() {
	initnode(1);
	head_insert(5);
	head_insert(2);
	end_insert(3);
	show();
	delete_node(2);
	show();
}

void initnode(int key) {
	Node* new_node = new Node;
	new_node->val = key;
	head = new Node;
	last = new Node;
	//将新结点连接到头结点和尾结点当中
	head->next = new_node;
	last->front = new_node;
	new_node->next = last;
	new_node->front = head;
	//将头尾结点相连
	head->front = last;
	last->next = head;
	head->val = last->val = -1;	//将两个结点赋一个不可能的值
}

void head_insert(int key) {
	Node* new_node = new Node;
	new_node->val = key;
	new_node->next = head->next;	//将新结点的next指向头结点的next
	new_node->front = head;			//将新结点的front指向头结点
	head->next->front = new_node;	//将头结点的下一个结点的front指向新结点
	head->next = new_node;			//将头结点的next指向新结点
}

void end_insert(int key) {
	Node* new_node = new Node;
	new_node->val = key;
	new_node->next = last;				//将新结点的next指向尾结点
	new_node->front = last->front;		//将新结点的front指向尾结点的上一个结点
	new_node->front->next = new_node;	//将新结点的上一个结点的下一个结点指向新结点
	last->front = new_node;				//将尾结点的front指向新结点
}

void delete_node(int key) {
	Node* temp = head->next;
	while (temp->val != key){
		//判断是否有该结点
		if (temp->val == -1){
			cout << "没有此结点" << endl;
			return;
		}
		temp = temp->next;
	}

	Node* Prev = temp->front;	//创建临时结点指向删除结点的上一个结点
	Node* Next = temp->next;	//创建临时结点指向删除结点的下一个结点
	Prev->next = Next;			//删除结点的上一个结点的next指向删除结点的下一个结点
	Next->front = Prev;			//删除结点的下一个节点的front指向删除结点的上一个结点
	/*
	也可以不用创建Prev和Next指针
	temp->front->next = temp->next;
	temp->next->front = temp->front;
	*/

	temp->front = temp->next = NULL;
	delete[]temp;
	temp = NULL;
}

void show()
{
	Node* temp = head->next;  //创建一个临时指针
	if (temp->val == -1)
	{
		cout << "当前还没有创建结点" << endl;
		return;
	}
	while (temp->val != -1)      //遍历整个链表
	{
		cout << temp->val << " ";
		temp = temp->next;
	}
	cout << endl;
}

如果大家有什么问题的话,欢迎在下方评论区进行讨论哦~

【上一讲】线性表 - 02 单向链表

【下一讲】线性表 - 04 栈

你可能感兴趣的:(数据结构与算法,链表,数据结构,c++)