Detailed Explanation of Leading Circular Doubly Linked List

Table of contents

1. What is the lead circular doubly linked list?

1. Features:

2. Advantages:

Second, implement the interface

1. Preparation

1.1 Three files required

1.2 Creation of structure and reference of header file

2. Interface implementation

2.1 function to create a new node

2.2 Print the content of the linked list

 2.3 Tail plug new node

2.4 Plug in new nodes

 2.5 Head delete node

2.6 Delete node at the end

 2.7 Finding Nodes

2.8 Insert a node before the specified position

2.9 Delete the specified location node.

2.10 Destroy the linked list

 3. All codes

1. Interface header file

2. Interface implementation

3. Test file


1. What is the lead circular doubly linked list?

1. Features:

1. Take the lead: There are sentinel nodes, which do not need to store data. When inserting and deleting operations on the linked list, the nodes will not be affected.

2. Two-way: The structure members in the structure that make up the linked list have data, the address of the previous node and the address of the next node

3. Loop: The head node of the linked list stores the address of the tail node, and the tail node of the linked list stores the address of the head node.

2. Advantages:

Compared with the single-way linked list, the advantage of the double-way circular linked list is that its tail insertion is very fast to find the tail     because its head node also stores the address of the previous node, and the previous node of the head is the tail. Compared with the headless linked list, the advantage of the headed linked list is that when there are no nodes, you can directly increase the corresponding pointer by accessing the members of the structure, and if you are headless, you need to modify the address directly, and you need to pass it when you pass the variable. The secondary pointer    is not only difficult to understand, but also easy to make mistakes in implementation.

Second, implement the interface

1. Preparation

1.1 Three files required

First create two .c files, and then create a header file named test.c, list.c, list.h

test.c is used to test the written interface                                   list.c stores the code for implementing the interface

list.h stores the declarations of corresponding functions, header files, and structures, so that when you want to use the interface of the linked list, you can directly refer to list.h without referencing other header files. 

The created environment is shown in the figure

1.2 Creation of structure and reference of header file

These contents are placed in the list.h file, and the references can be taken away in one go, and there is no need to quote other contents

#pragma once//防止头文件二次引用
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int LTDateType;
//这样创建结构体数据类型,不仅是为了和int做区分
//也是为了到时更好的替换,想换直接把int改了就行
typedef struct listnode
{
	struct listnode* prev;//存放上一个节点的地址
	struct listnode* next;//存放下一个节点的地址
	LTDateType data;//该节点存放的数据
}listnode;

2. Interface implementation

2.1 function to create a new node

Creating a node is simple, but we will use it in many operations, so it is separately packaged into an interface

listnode* buy_listnode(LTDateType x)
{
	listnode*newnode=(listnode*)malloc(sizeof(listnode));
	if (newnode == NULL)
	{
		perror("buy_listnode");//错误提示
		exit(-1);//节点都没创建出来,直接退出程序
	}
	newnode->data = x;//将新节点的数据初始化成我们需要的
	newnode->next = NULL;//不清楚插入的方式,先初始化成空
	newnode->prev = NULL;
}

2.2 Print the content of the linked list

Very classic operation, just traverse the linked list. The only thing to note is that the sentinel node is not a real member of the linked list, it can only be regarded as an auxiliary to realize the linked list, so the sentinel node is skipped for printing

void print_list(listnode* phead)
{
	assert(phead);//哨兵节点地址不可能为空
	listnode* head = phead->next;
	//哨兵位节点不存储有效数据,因此phead->next才是头节点
	printf("head<=>");//纯属美观
	while (head != phead)//当head和phead相等意味着已经遍历完一遍链表
	{
		printf("%d<=>", head->data);
		head = head->next;
	}
	printf("\n");
}

 2.3 Tail plug new node

void list_pushback(listnode*phead,LTDateType x)
//尾插一个新节点,此节点存储x
{
	listnode* newnode = buy_listnode(x);
	//创建一个我们需要的新节点
	listnode* tail = phead->prev;
	//先找尾,尾很显然是哨兵位节点的上一个节点
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

The following 4 lines of code are the core, explained separately in the article, create a new node, put it at the end of the linked list, we have found the tail node, the next step is the link

First of all, it is clear that the new tail is a new node created, but before the link is made, the tail is still the previous tail

original linked list

first step: 

Step two: 

 

third step:

 the fourth step:

Test code:

#include"list.h"
void test1()
{
	listnode* plist=NULL;
	plist=init_listnode(plist);
	list_pushback(plist,1);
	list_pushback(plist,2);
	list_pushback(plist,3);
	list_pushback(plist,4);
	print_list(plist);
}
int main()
{
	test1();
}

 Test results:

2.4 Plug in new nodes

I won’t draw any more pictures here. It’s faster to draw once than to watch others draw ten thousand times. 

void list_pushfront(listnode* phead, LTDateType x)
{
	listnode* head = phead->next;//找到头节点
	listnode* newnode = buy_listnode(x);//创建新节点
	head->prev = newnode;
	newnode->next = head;
	phead->next = newnode;
	newnode->prev = phead;
}

Test code:

void test2()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	print_list(plist);
	list_pushfront(plist, 10086);
	print_list(plist);
}
int main()
{
	test2();
}

Test results: 

 2.5 Head delete node

One thing to note is that the node we delete is not a sentinel node. The sentinel node does not store valid data. What we delete is the head node

void list_popfront(listnode*phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("链表为空,操作失败\n");//为空就别删了
		return;
	}
	listnode* head = phead->next;//找到头节点
	phead->next = head->next;
	head->next->prev = phead;
	free(head);//链接完成,彻底删除
}

Test code:

void test3()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	print_list(plist);
	list_pushfront(plist, 10086);
	print_list(plist);
	list_popfront(plist);
	print_list(plist);
	list_popfront(plist);
	print_list(plist);
	list_popfront(plist);
	print_list(plist);
	list_popfront(plist);
	print_list(plist);
}
int main()
{
	test3();
}

Test results:

 

2.6 Delete node at the end

There is nothing to say, just like before, the key point is on the link, and I know everything after drawing the picture myself

void list_popback(listnode*phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("链表为空,操作失败\n");//为空就别删了
		return;
	}
	listnode* tail = phead->prev;//找到尾节点
	phead->prev = tail->prev;
	tail->prev->next = phead;
	free(tail);
}

Test code:

void test4()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	list_pushfront(plist, 10086);
	print_list(plist);
	list_popback(plist);
	print_list(plist);
	list_popback(plist);
	print_list(plist);
	list_popback(plist);
	print_list(plist);
	list_popback(plist);
	print_list(plist);
}
int main()
{
	test4();
}

Test results: 

 2.7 Finding Nodes

Traverse once, return NULL if not found

listnode* list_find(listnode* phead, LTDateType x)
//哨兵节点和目标
{
	assert(phead);
	listnode* head = phead->next;//找到头节点
	while (head!=phead)//相等意味着已经遍历完了
	{
		if (head->data == x)
		//找到目标,直接返回
		{
			return head;
		}
		head = head->next;
	}
	return NULL;//遍历完还找不到,返回空指针
}

2.8 Insert a node before the specified position

Just link according to the target

void list_insert(listnode*pos,LTDateType x)
//目标位置,和在其前面插入数据为x的节点
{
	if (pos == NULL)//传空意味着没找到目标
	{
		printf("目标不存在,操作失败\n");
		return;
	}
	listnode*newnode=buy_listnode(x);//创建新节点
	newnode->next = pos;
	newnode->prev= pos->prev;
	pos->prev->next = newnode;
	pos->prev = newnode;
}

Test code:

void test5()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	list_pushfront(plist, 10086);
	print_list(plist);
	listnode*pos=list_find(plist,2);
	list_insert(pos, 888);//在2之前插入888
	print_list(plist);
	list_insert(plist->next, 666);
	//在头节点前插入666,与头插效用一致
	//可以在头插中复用这个函数
	print_list(plist);
	list_insert(plist, 520);
	//在哨兵节点前插入520,与尾插效用一致
	//可以在尾插中复用这个函数
	print_list(plist);


}
int main()
{
	test5();
}

Test results:

2.9 Delete the specified location node.

void list_erase(listnode* pos)
{
	assert(pos && pos->next != pos);
    //pos为空意味着不存在,pos->next==pos意味着为哨兵节点
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
}

Test code:

void test6()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	//list_erase(plist->prev);//尾删,测试报错
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	list_pushfront(plist, 3);
	list_pushfront(plist, 4);
	list_pushfront(plist, 5);
	print_list(plist);
	listnode* pos = list_find(plist, 2);
	list_erase(pos);//把2删除
	print_list(plist);
	list_erase(plist->next);//头删
	print_list(plist);
	list_erase(plist->prev);//尾删
	print_list(plist);
}
int main()
{
	test6();
}

Test results:

2.10 Destroy the linked list

void destory_list(listnode* phead)
{
	listnode* tail = phead->prev;
	while (tail != phead)
	{
		listnode* tmp = tail;//存储尾
		tail = tail->prev;//从后往前遍历
		free(tmp);
		//不需要管什么链接了,直接摧毁就行

	}
	free(phead);//单独摧毁
}

 Test code:
 

void test7()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	//list_erase(plist->prev);//尾删,测试报错
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	list_pushfront(plist, 3);
	list_pushfront(plist, 4);
	list_pushfront(plist, 5);
	destory_list(plist);
}
int main()
{
	test7();
}

Test results:

From the point of view of monitoring, it is true that all of them were released

 3. All codes

1. Interface header file

#pragma once//防止头文件二次引用
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int LTDateType;
//这样创建结构体数据类型,不仅是为了和int做区分
//也是为了到时更好的替换,想换直接把int改了就行
typedef struct listnode
{
	struct listnode* prev;//存放上一个节点的地址
	struct listnode* next;//存放下一个节点的地址
	LTDateType data;//该节点存放的数据
}listnode;
listnode* buy_listnode(LTDateType x);
listnode* init_listnode(listnode* phead);
void print_list(listnode* phead);
void list_pushback(listnode* phead, LTDateType x);
void list_pushfront(listnode* phead, LTDateType x);
void list_popfront(listnode* phead);
void list_popback(listnode* phead);
listnode* list_find(listnode* phead, LTDateType x);
void list_insert(listnode* pos, LTDateType x);
void list_erase(listnode* pos);
void destory_list(listnode* phead);

2. Interface implementation

#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
listnode* buy_listnode(LTDateType x)
{
	listnode*newnode=(listnode*)malloc(sizeof(listnode));
	if (newnode == NULL)
	{
		perror("buy_listnode");//错误提示
		exit(-1);//节点都没创建出来,直接退出程序
	}
	newnode->data = x;//将新节点的数据初始化成我们需要的
	newnode->next = NULL;//不清楚插入的方式,先初始化成空
	newnode->prev = NULL;
}
listnode* init_listnode(listnode* phead)
{
	phead = buy_listnode(-1);	//-1是随便给的,初始化哨兵节点中的数据为-1,代表着没意义的数据
	phead->next = phead;//初始化哨兵节点,自己指向自己
	phead->prev = phead;
	return phead;
}
void print_list(listnode* phead)
{
	assert(phead);//哨兵节点地址不可能为空
	listnode* head = phead->next;
	//哨兵位节点不存储有效数据,因此phead->next才是头节点
	printf("head<=>");//纯属美观
	while (head != phead)//当head和phead相等意味着已经遍历完一遍链表
	{
		printf("%d<=>", head->data);
		head = head->next;
	}
	printf("\n");
}
void list_pushback(listnode*phead,LTDateType x)
//尾插一个新节点,此节点存储x
{
	listnode* newnode = buy_listnode(x);
	//创建一个我们需要的新节点
	listnode* tail = phead->prev;
	//先找尾,尾很显然是哨兵位节点的上一个节点
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}
void list_pushfront(listnode* phead, LTDateType x)
{
	listnode* head = phead->next;//找到头节点
	listnode* newnode = buy_listnode(x);//创建新节点
	head->prev = newnode;
	newnode->next = head;
	phead->next = newnode;
	newnode->prev = phead;
}
void list_popfront(listnode*phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("链表为空,操作失败\n");//为空就别删了
		return;
	}
	listnode* head = phead->next;//找到头节点
	phead->next = head->next;
	head->next->prev = phead;
	free(head);//链接完成,彻底删除
}
void list_popback(listnode*phead)
{
	assert(phead);
	if (phead->next == phead)
	{
		printf("链表为空,操作失败\n");//为空就别删了
		return;
	}
	listnode* tail = phead->prev;//找到尾节点
	phead->prev = tail->prev;
	tail->prev->next = phead;
	free(tail);
}
listnode* list_find(listnode* phead, LTDateType x)
//哨兵节点和目标
{
	assert(phead);
	listnode* head = phead->next;//找到头节点
	while (head!=phead)//相等意味着已经遍历完了
	{
		if (head->data == x)
		//找到目标,直接返回
		{
			return head;
		}
		head = head->next;
	}
	return NULL;//遍历完还找不到,返回空指针
}
void list_insert(listnode*pos,LTDateType x)
//目标位置,和在其前面插入数据为x的节点
{
	if (pos == NULL)//传空意味着没找到目标
	{
		printf("目标不存在,操作失败\n");
		return;
	}
	listnode*newnode=buy_listnode(x);//创建新节点
	newnode->next = pos;
	newnode->prev= pos->prev;
	pos->prev->next = newnode;
	pos->prev = newnode;
}
void list_erase(listnode* pos)
{
	assert(pos && pos->next != pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
}
void destory_list(listnode* phead)
{
	listnode* tail = phead->prev;
	while (tail != phead)
	{
		listnode* tmp = tail;//存储尾
		tail = tail->prev;//从后往前遍历
		free(tmp);
		//不需要管什么链接了,直接摧毁就行

	}
	free(phead);//单独摧毁
}

3. Test file

#define _CRT_SECURE_NO_WARNINGS 1
#include"list.h"
void test1()
{
	listnode* plist=NULL;
	plist=init_listnode(plist);
	list_pushback(plist,1);
	list_pushback(plist,2);
	list_pushback(plist,3);
	list_pushback(plist,4);
	print_list(plist);
}
void test2()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	print_list(plist);
	list_pushfront(plist, 10086);
	print_list(plist);
}
void test3()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	print_list(plist);
	list_pushfront(plist, 10086);
	print_list(plist);
	list_popfront(plist);
	print_list(plist);
	list_popfront(plist);
	print_list(plist);
	list_popfront(plist);
	print_list(plist);
	list_popfront(plist);
	print_list(plist);
}
void test4()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	list_pushfront(plist, 10086);
	print_list(plist);
	list_popback(plist);
	print_list(plist);
	list_popback(plist);
	print_list(plist);
	list_popback(plist);
	print_list(plist);
	list_popback(plist);
	print_list(plist);
}
void test5()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	list_pushfront(plist, 10086);
	print_list(plist);
	listnode*pos=list_find(plist,2);
	list_insert(pos, 888);//在2之前插入888
	print_list(plist);
	list_insert(plist->next, 666);
	//在头节点前插入666,与头插效用一致
	//可以在头插中复用这个函数
	print_list(plist);
	list_insert(plist, 520);
	//在哨兵节点前插入520,与尾插效用一致
	//可以在尾插中复用这个函数
	print_list(plist);
}
void test6()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	//list_erase(plist->prev);//尾删,测试报错
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	list_pushfront(plist, 3);
	list_pushfront(plist, 4);
	list_pushfront(plist, 5);
	print_list(plist);
	listnode* pos = list_find(plist, 2);
	list_erase(pos);//把2删除
	print_list(plist);
	list_erase(plist->next);//头删
	print_list(plist);
	list_erase(plist->prev);//尾删
	print_list(plist);
}
void test7()
{
	listnode* plist = NULL;
	plist = init_listnode(plist);
	//list_erase(plist->prev);//尾删,测试报错
	list_pushfront(plist, 1);
	list_pushfront(plist, 2);
	list_pushfront(plist, 3);
	list_pushfront(plist, 4);
	list_pushfront(plist, 5);
	destory_list(plist);
}
int main()
{
	test7();
}

Well, today’s sharing is over here, thank you friends for visiting, I wish you all a bright future O(∩_∩)O

Guess you like

Origin blog.csdn.net/fq157856469/article/details/132076019