[Data structure and algorithm] Realize the leading bidirectional circular linked list (the most complex linked list)

In the previous article, we realized the structure of the linked list, and realized the headless one-way non-circular linked list. Next, we implemented another commonly used linked list structure, the leading two-way circular linked list. If you still don’t understand the one-way linked list, please read this article (7 messages) [Data structure and algorithm] Know the linked list in the linear list and realize the one-way linked list_Xiaowang Xuecode Blog-CSDN Blog

Table of contents

foreword

1. What is the leading two-way circular linked list?

2. Realize the leading two-way circular linked list

1. The structure and the function to be implemented

2. Initialize and print the linked list

3. Head plug and tail plug

4. Head delete and tail delete

5. Find and return the number of nodes

6. Insert a node before the pos position

7. Delete the specified pos node

8. Destroy the linked list

3. Complete code

1.DSLinkList.h

2.DSLinkList.c

3.test.c

Summarize


foreword

The leading two-way circular linked list is the most complex structure in the linked list. The main functions we implement are: inserting the head and inserting the tail, deleting the head and deleting the tail, initialization, printing, specifying pos position to insert or delete nodes, and finding nodes , Destroy linked list and other functions.


1. What is the leading two-way circular linked list?

as the picture shows:


2. Realize the leading two-way circular linked list

1. The structure and the function to be implemented

The structure is as follows:

typedef struct DSLDataType;
//定义双向链表的结构体

//双向链表每一个节点都有一个前驱节点和后继节点,可以根据节点获得其前后的节点
typedef struct DSListNode {
	struct DSListNode* prev;//前驱节点
	struct DSListNode* next;//后继节点
	int value;//数据域
}DSLNode;//重定义

Compared with the one-way linked list, there is one more pointer field prev, pointing to the previous node address

The function that implements the function:

//初始化
DSLNode*ListInit();
//打印链表
void ListPrint(DSLNode* ps);
//尾部插入
void ListPushBack(DSLNode* ps, int data);
//头部插入
void ListPushFront(DSLNode* ps, int data);
//尾部删除
void ListPopBack(DSLNode* ps);
//头部删除
void ListPopFront(DSLNode* ps);
//判空
bool ListEmpty(DSLNode* ps);
//返回链表节点个数
int Listsize(DSLNode* ps);
//寻找指定元素,返回对应的节点
DSLNode* ListFind(DSLNode* ps, int data);
//在pos之前插入节点
void ListInsert(DSLNode* pos, int data);
//删除pos位置结点
void ListEarse(DSLNode* pos);
//摧毁链表
void ListDestory(DSLNode* ps);

2. Initialize and print the linked list

//对于函数的实现
//初始化
DSLNode* ListInit() {
	//初始化创建链表
	//创建新节点
	DSLNode* num = (DSLNode*)malloc(sizeof(DSLNode));
	//判断是否创建成功
	if (num == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	num->prev = num;
	num->next = num;//作为头节点,前驱和后驱结点都指向自己
	return num;
}

//打印链表
void ListPrint(DSLNode* ps) {
	assert(ps);//断言
	DSLNode* cur = ps->next;
	printf("phead->");
	while (cur != ps) {//双向链表,回到ps,就表示转了一圈
		printf("%d->", cur->value);
		cur = cur->next;
	}
	printf("NULL\n");
}

3. Head plug and tail plug

The tail plug is shown in the figure:

code show as below:


DSLNode* CreatNode(int data) {//创建新节点
	DSLNode* cur = (DSLNode*)malloc(sizeof(DSLNode));
	if (cur == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	cur->next = cur->prev = NULL;
	cur->value = data;
}
//尾部插入
void ListPushBack(DSLNode* ps, int data) {
	assert(ps);//断言
	DSLNode* newnode = CreatNode(data);
	DSLNode* tail = ps->prev;//把头节点之前的那个地址给tail
	tail->next = newnode;//将ps之前的那个数值,实际上是这个双向链表的最后一个元素的位置,的next指针指向新节点
	newnode->prev = tail;//新节点前后为 tail和ps
	newnode->next = ps;
	ps->prev = newnode;//tail的两个指针域都指向完成,就只剩下ps的前驱指针

}

The head plug is shown in the figure:
 the code is as follows:


//头部插入
void ListPushFront(DSLNode* ps, int data) {
	assert(ps);
	//头部插入就是将ps之后的一个地址给临时变量tail
	DSLNode* tail = ps->next;
	DSLNode* newnode = CreatNode(data);//创建新节点
	ps->next->prev = newnode;
	newnode->next = ps->next;
    newnode->prev = ps;
    ps->next = newnode;
}

4. Head delete and tail delete

Tail deletion is shown in the figure:

code show as below:

//判空
bool ListEmpty(DSLNode* ps) {
	assert(ps);//断言
	return ps->next == ps;//如果是只有一个ps头节点,则表示为空,返回true,否则返回false

}
//尾部删除
void ListPopBack(DSLNode* ps) {

	assert(ps);//断言
	assert(!ListEmpty(ps));//先判断是否为空
	DSLNode* cur = ps->prev;
	//将cur的next值为NULL
	ps->prev = ps->prev->prev;
	ps->prev->next = ps;
	//这是直接略过尾部最后一个元素
	//跳过ps之前的一个节点,先让其左右节点相连,然后释放这个地址
	free(cur);
	cur = NULL;
}

 The header is deleted as shown in the figure:

code show as below:

//头部删除
void ListPopFront(DSLNode* ps) {
	assert(ps);
	assert(!ListEmpty(ps));
	DSLNode* cur = ps->next;
	//头删  是将头节点之后的第一个节点删除释放空间
	ps->next = ps->next->next;
	ps->next->prev = ps;
	free(cur);
	cur = NULL;
}

5. Find and return the number of nodes

Search: return a node, according to the data value field, return the first encountered node cur, if not return NULL

//返回链表节点个数
int Listsize(DSLNode* ps) {
	assert(ps);
	int count = 0;
	DSLNode* cur = ps->next;
	while (cur != ps) {
		count++;
		cur = cur->next;
	}
	return count;
}
//查找
DSLNode* ListFind(DSLNode* ps, int data) {
	assert(ps);
	DSLNode* cur = ps->next;
	while (cur != ps) {
		if (cur->value == data) {
			return cur;
		}
	}
	return NULL;//NULL表示不存在
}

6. Insert a node before the pos position

as the picture shows:

 code show as below:

//插入节点
void ListInsert(DSLNode* pos, int data) {

	assert(pos);
	//pos三种可能
	//1.空链表
	//2.只有一个节点
	DSLNode* cur = pos->prev;
	//双向链表可以直接找到pos之前的位置,单向链表只能进行循环
	DSLNode* newnode = CreatNode(data);
	pos->prev = newnode;//把新节点newnode的位置给pos的prev
	newnode->prev = cur;//cur表示的是创建new节点之前的时候pos之前的结点
	cur->next = newnode;
	newnode->next = pos;
	//另一种不使用cur的方法
	//newnode->next = pos;
	//pos->prev->next = newnode;
	//newnode->prev = pos->prev;
	//pos->prev = newnode;
}

7. Delete the specified pos node

as the picture shows:

code show as below:


//删除指针
void ListEarse(DSLNode* pos) {
	assert(pos);
	DSLNode* cur = pos->prev;
	DSLNode* tail = pos->next;
	cur->next = tail;//两者相互指向,最后释放pos空间
	tail->prev = cur;
	free(pos);
	pos = NULL;
}

8. Destroy the linked list

Two ways, you can use the secondary pointer directly, or you can use the primary pointer one by one free and NULL

//摧毁链表
void ListDestory(DSLNode* ps) {
	//可以设计二级指针直接将ps地址滞空为NULL
	//这里还是使用一级指针
	assert(ps);
	DSLNode* cur = ps->next;
	while (cur != ps) {
		DSLNode* tail = cur->next;//这个地方就是一个精彩的地方
		free(cur);//使用临时变量tail得到cur的下一个地址,然后再free cur
		cur = tail;//tail这个时候就是cur 的下一个结点
	}
	free(ps);
	ps = NULL;

}
void ListDestory2(DSLNode** ps) {
	assert(ps);
	free(ps);//二级指针直接free
	*ps = NULL;
}

3. Complete code

1.DSLinkList.h

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<assert.h>
#include<stdbool.h>

typedef struct DSLDataType;
//定义双向链表的结构体

//双向链表每一个节点都有一个前驱节点和后继节点,可以根据节点获得其前后的节点
typedef struct DSListNode {
	struct DSListNode* prev;//前驱节点
	struct DSListNode* next;//后继节点
	int value;//数据域
}DSLNode;//重定义

//创建头节点,并将tail和head都指向第一个节点
struct DSList {
	struct DSListNode* tail;
	struct DSListNode* head;
	unsigned int len;//表示链表的长度
};
//初始化
DSLNode*ListInit();
//打印链表
void ListPrint(DSLNode* ps);
//尾部插入
void ListPushBack(DSLNode* ps, int data);
//头部插入
void ListPushFront(DSLNode* ps, int data);
//尾部删除
void ListPopBack(DSLNode* ps);
//头部删除
void ListPopFront(DSLNode* ps);
//判空
bool ListEmpty(DSLNode* ps);
//返回链表节点个数
int Listsize(DSLNode* ps);
//寻找指定元素,返回对应的节点
DSLNode* ListFind(DSLNode* ps, int data);
//在pos之前插入节点
void ListInsert(DSLNode* pos, int data);
//删除pos位置结点
void ListEarse(DSLNode* pos);
//摧毁链表
void ListDestory(DSLNode* ps);

2.DSLinkList.c

#define _CRT_SECURE_NO_WARNINGS

#include"DSLinkList.h"

//对于函数的实现
//初始化
DSLNode* ListInit() {
	//初始化创建链表
	//创建新节点
	DSLNode* num = (DSLNode*)malloc(sizeof(DSLNode));
	//判断是否创建成功
	if (num == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	num->prev = num;
	num->next = num;
	return num;
}

//打印链表
void ListPrint(DSLNode* ps) {
	assert(ps);//断言
	DSLNode* cur = ps->next;
	printf("phead->");
	while (cur != ps) {//双向链表,回到ps,就表示转了一圈
		printf("%d->", cur->value);
		cur = cur->next;
	}
	printf("NULL\n");
}

DSLNode* CreatNode(int data) {//创建新节点
	DSLNode* cur = (DSLNode*)malloc(sizeof(DSLNode));
	if (cur == NULL) {
		perror("malloc fail\n");
		exit(-1);
	}
	cur->next = cur->prev = NULL;
	cur->value = data;
}
//尾部插入
void ListPushBack(DSLNode* ps, int data) {
	assert(ps);//断言
	DSLNode* newnode = CreatNode(data);
	DSLNode* tail = ps->prev;//把头节点之前的那个地址给tail
	tail->next = newnode;//将ps之前的那个数值,实际上是这个双向链表的最后一个元素的位置,的next指针指向新节点
	newnode->prev = tail;//新节点前后为 tail和ps
	newnode->next = ps;
	ps->prev = newnode;//tail的两个指针域都指向完成,就只剩下ps的前驱指针

}

//头部插入
void ListPushFront(DSLNode* ps, int data) {
	assert(ps);
	//头部插入就是将ps之后的一个地址给临时变量tail
	DSLNode* tail = ps->next;
	DSLNode* newnode = CreatNode(data);//创建新节点
	ps->next->prev = newnode;
	newnode->next = ps->next;
    newnode->prev = ps;
    ps->next = newnode;
}

//判空
bool ListEmpty(DSLNode* ps) {
	assert(ps);//断言
	return ps->next == ps;//如果是只有一个ps头节点,则表示为空,返回true,否则返回false

}

//返回链表节点个数
int Listsize(DSLNode* ps) {
	assert(ps);
	int count = 0;
	DSLNode* cur = ps->next;
	while (cur != ps) {
		count++;
		cur = cur->next;
	}
	printf("\n");
	return count;
}
//尾部删除
void ListPopBack(DSLNode* ps) {

	assert(ps);//断言
	assert(!ListEmpty(ps));//先判断是否为空
	DSLNode* cur = ps->prev;
	//将cur的next值为NULL
	ps->prev = ps->prev->prev;
	ps->prev->next = ps;
	//这是直接略过尾部最后一个元素
	//跳过ps之前的一个节点,先让其左右节点相连,然后释放这个地址
	free(cur);
	cur = NULL;
}
//头部删除
void ListPopFront(DSLNode* ps) {
	assert(ps);
	assert(!ListEmpty(ps));
	DSLNode* cur = ps->next;
	//头删  是将头节点之后的第一个节点删除释放空间
	ps->next = ps->next->next;
	ps->next->prev = ps;
	free(cur);
	cur = NULL;
}
//查找
DSLNode* ListFind(DSLNode* ps, int data) {
	assert(ps);
	DSLNode* cur = ps->next;
	while (cur != ps) {
		if (cur->value == data) {
			return cur;
		}
	}
	return NULL;//NULL表示不存在
}
//插入节点
void ListInsert(DSLNode* pos, int data) {

	assert(pos);
	//pos三种可能
	//1.空链表
	//2.只有一个节点
	DSLNode* cur = pos->prev;
	//双向链表可以直接找到pos之前的位置,单向链表只能进行循环
	DSLNode* newnode = CreatNode(data);
	pos->prev = newnode;//把新节点newnode的位置给pos的prev
	newnode->prev = cur;//cur表示的是创建new节点之前的时候pos之前的结点
	cur->next = newnode;
	newnode->next = pos;
	//另一种不使用cur的方法
	//newnode->next = pos;
	//pos->prev->next = newnode;
	//newnode->prev = pos->prev;
	//pos->prev = newnode;
}


//删除指针
void ListEarse(DSLNode* pos) {
	assert(pos);
	DSLNode* cur = pos->prev;
	DSLNode* tail = pos->next;
	cur->next = tail;//两者相互指向,最后释放pos空间
	tail->prev = cur;
	free(pos);
	pos = NULL;
}
//摧毁链表
void ListDestory(DSLNode* ps) {
	//可以设计二级指针直接将ps地址滞空为NULL
	//这里还是使用一级指针
	assert(ps);
	DSLNode* cur = ps->next;
	while (cur != ps) {
		DSLNode* tail = cur->next;
		free(cur);
		cur = tail;
	}
	free(ps);
	ps = NULL;

}
void ListDestory2(DSLNode** ps) {
	assert(ps);
	free(ps);
	*ps = NULL;
}

3.test.c

#define _CRT_SECURE_NO_WARNINGS
#include"DSLinkList.h"

void test()
{
	DSLNode* phead = ListInit();//初始化
	
	ListPushBack(phead, 1);
	ListPushBack(phead, 2);
	ListPushBack(phead, 3);
	ListPushBack(phead, 4);
	ListPushBack(phead, 5);//检验尾插
	ListPrint(phead);//打印

	ListPushFront(phead, 10);
	ListPushFront(phead, 20);
	ListPushFront(phead, 30);
	ListPushFront(phead, 40);
	ListPushFront(phead, 50);//检验头插
	ListPrint(phead);//打印
	printf("%d\n", Listsize(phead));//检验返回结点个数

	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);//尾删
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);//头删
	ListPrint(phead);//打印
	ListInsert(phead->next, 10);//pos指定结点之前插入newnode新节点
	ListPrint(phead);//打印
}
int main()
{
	test();
	return 0;
}

Summarize

This article mainly explains the use and code implementation of the most complex leading two-way circular linked list in the linked list. point, destroy linked list and other functions. And made a blackboard diagram to explain the flow of the corresponding function, with complete code to help everyone better understand.

Next, we will learn about stacks and queues. For the shortcomings of this article, welcome to guide us one or two, and let us start the study of a new chapter! ! !

Guess you like

Origin blog.csdn.net/qq_63319459/article/details/128770646