Data structure - two-way circular list with head node

The doubly linked list with the head node is a special kind of doubly linked list. Compared with the ordinary doubly linked list, the biggest difference is that the next pointer of the head node of the linked list no longer points to the first actual node, but points to the first node in the linked list. a node. At the same time, the prev pointer of the tail node of the linked list no longer points to NULL, but points to the last node in the linked list.

In addition, compared with ordinary doubly linked lists, when inserting and deleting nodes, the bidirectional circular linked list with the head node needs to pay special attention to the pointer processing of the head node and the tail node to ensure the circularity of the linked list .
insert image description here

typedef int LTDatatype;
typedef struct ListNode
{
    
    
	struct ListNode* next;
	struct ListNode* prev;
	LTDatatype data;
}LTNode;

This code fragment defines a structure named ListNode with three members:

next: A pointer to the next node in the linked list.
prev: A pointer to the previous node in the linked list.
data: LTDatatype variable for storing node data.
The typedef int LTDatatype; statement creates an alias for the int datatype named LTDatatype. This allows the programmer to easily modify the data types used in the linked list by changing the typedef statement rather than modifying the entire code.

Overall, this code simply defines a structure for the C language doubly linked list nodes.

//双向列表的初始化
LTNode* LTInit()
{
    
    
	LTNode* plist= BuyListNode(-1);
	plist->next = plist;
	plist->prev = plist;
	return plist;
}

This is a C language function used to initialize a doubly linked list. The return value of the function is a pointer to the head node of the linked list. The function name is LTInit, which means "initializing the linked list".

// 双向链表销毁
void ListDestory(LTNode* plist) {
    
    
	LTNode* p = plist; // 从头节点开始遍历
	while (p != NULL) {
    
    
		LTNode* tmp = p; // 保存当前节点指针
		p = p->next; // 移动到下一个节点
		free(tmp); // 释放当前节点的内存
	}
	plist = NULL; // 将头结点指针置为 NULL
}

This is a C language function used to destroy a doubly linked circular list with the head node. The parameter of the function is the pointer plist pointing to the head node of the linked list, and the function has no return value. The function name is ListDestory, which means "destroy the linked list".

Note that in the bidirectional circular linked list with the head node, the next pointer of the head node no longer points to the first actual node, but points to the first node in the linked list. Therefore, when destroying the linked list, it is necessary to traverse from the node pointed to by the next pointer of the head node until returning to the head node. At the same time, before releasing the memory of the head node, the memory of all nodes in the linked list needs to be released first.

// 双向链表打印
void ListPrint(LTNode* plist)
{
    
    
	assert(plist);
	printf("<=head=>");
	LTNode* cur = plist->next;
	while (cur != plist)
	{
    
    
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

This is a C language function used to print the data of all nodes in a doubly circular linked list with the head node. The parameter of the function is the pointer plist pointing to the head node of the linked list, and the function has no return value. The function name is ListPrint, which means "print a linked list".

Note that in the bidirectional circular linked list with the head node, the next pointer of the head node no longer points to the first actual node, but points to the first node in the linked list. Therefore, when printing the linked list, it is necessary to traverse from the node pointed to by the next pointer of the head node until returning to the head node. At the same time, when outputting node data, it is necessary to distinguish between the head node and the actual node to avoid confusion.

bool LTEmpty(LTNode* plist)
{
    
    
    assert(plist != NULL); // 确保链表头结点不为空
    return plist->next == plist; // 如果头结点的 next 指针指向自身,说明链表为空,返回 true,否则返回 false
}

This is a C language function used to determine whether a doubly linked circular list with a head node is empty. The parameter of the function is the pointer plist pointing to the head node of the linked list, and the function returns a boolean value. The function name is LTEmpty, which means "whether the linked list is empty".

// 双向链表尾删
void ListPopBack(LTNode* plist)
{
    
    
	assert(plist);
	assert(!LTEmpty(plist));
	LTNode* tail = plist->prev;
	LTNode* tailPrev = tail->prev;
	tailPrev->next = plist;
	plist->prev = tailPrev;
	free(tail);
	tail = NULL;
}

This is a C language function used to delete the last node in the doubly linked list with the head node. The parameter of the function is the pointer plist pointing to the head node of the linked list, and the function has no return value. The function name is ListPopBack, which means "tail delete linked list".

The implementation of the function usually involves the following steps:
check whether the head node of the linked list and the linked list are empty, and if they are empty, throw an exception.
Find the last node in the linked list, which is the tail node.
Find the predecessor node of the tail node, and point its next pointer to the head node, and point the prev pointer of the head node to the predecessor node of the tail node.
Free the memory of the end node and set its pointer to NULL.

// 双向链表头插
void ListPushFront(LTNode* plist, LTDataType x)
{
    
    
    assert(plist != NULL); // 确保链表头结点不为空
    LTNode* newnode = BuyListNode(x); // 创建新节点
    newnode->next = plist->next; // 将新节点的 next 指针指向头结点的下一个节点
    plist->next->prev = newnode; // 将头结点的下一个节点的 prev 指针指向新节点
    plist->next = newnode; // 将头结点的 next 指针指向新节点
    newnode->prev = plist; // 将新节点的 prev 指针指向头结点
}

This is a C language function used to insert a new node at the head of a doubly linked list with the head node. The parameters of the function are the pointer plist pointing to the head node of the linked list and the node data x to be inserted, and the function has no return value. The function name is ListPushFront, which means "head-inserted linked list".

// 双向链表尾插
void ListPushBack(LTNode* plist, LTDataType x)
{
    
    
	assert(plist);
	/*LTNode* newnode = BuyListNode(x);
	LTNode* tail = plist->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = plist;
	plist->prev = newnode;*/
	ListInsert(plist, x);
}

This is a C function that inserts a new node at the tail of a doubly linked list with the head node. The parameters of the function are the pointer plist pointing to the head node of the linked list and the node data x to be inserted, and the function has no return value. The function name is ListPushBack, which means "tail push linked list".

The implementation of a function usually involves the following steps:

Check whether the head node of the linked list is empty, and if it is empty, throw an exception.
Create a new node, and point its next pointer to the head node, and point the next pointer of the predecessor node of the head node to the new node.
Point the prev pointer of the new node to the predecessor node of the head node, and point the prev pointer of the head node to the new node.

// 双向链表头删
void ListPopFront(LTNode* plist) {
    
    
	if (plist == NULL) {
    
    
		return; // 如果链表为空,直接返回
	}

	LTNode* p = plist; // 保存头节点指针
	plist = plist->next; // 将下一个节点设置为新的头节点
	if (plist != NULL) {
    
    
		plist->prev = NULL; // 如果新的头节点不为空,则将其前驱节点的指针设置为 NULL
	}
	free(p); // 释放原始头节点的内存
}

This is a C language function that deletes the first node in a doubly linked list with the head node. The parameter of the function is the pointer plist pointing to the head node of the linked list, and the function has no return value. The function name is ListPopFront, which means "head delete linked list".

The implementation of a function usually involves the following steps:

Check whether the head node of the linked list is empty, if it is empty, return directly.
Set the node next to the head node as the new head node, and save the pointer to the original head node.
If the new head node is not null, set the pointer of its predecessor node to NULL.
Free the memory of the original head node and set its pointer to NULL.

// 双向链表查找
LTNode* ListFind(LTNode* plist, LTDataType x) 
{
    
    
	LTNode* p = plist->next; // 从第一个节点开始遍历
	while (p != NULL && p->data != x) {
    
     // 遍历链表,直到找到指定数据或到达链表结尾
		p = p->next; // 移动到下一个节点
	}
	return p; // 返回找到的节点的指针,如果未找到则返回 NULL
}

This is a C language function used to find the node of the specified data in the doubly linked circular list with the head node. The parameters of the function are the pointer plist pointing to the head node of the linked list and the data x to be searched, and the function returns the pointer of the found node, or NULL if not found. The function name is ListFind, which means "find a linked list".

The implementation of a function usually involves the following steps:

Start traversing the linked list from the first node.
When traversing the linked list, if a node whose data value is equal to x is found, return the pointer of the node.
If no matching node is found after traversing to the end of the linked list, NULL is returned.

// 双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x)
{
    
    
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* newnode = BuyListNode(x);
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

This is a C language function used to insert a new node in front of the specified node in the doubly linked circular list with the head node. The parameters of the function are the pointer pos pointing to the specified node and the node data x to be inserted, and the function has no return value. The function name is ListInsert, which means "insert into a linked list".

The implementation of a function usually involves the following steps:

Checks whether the specified node is empty, and throws an exception if it is empty.
Create a new node, and point its next pointer to the specified node, and point the next pointer of the predecessor node of the specified node to the new node.
Point the prev pointer of the new node to the predecessor node of the specified node, and point the prev pointer of the specified node to the new node.

// 双向链表删除pos位置的结点
void ListErase(LTNode* pos) 
{
    
    
	if (pos == NULL || pos->prev == NULL) {
    
    
		return; // 如果待删除节点为 NULL 或者是头结点,则直接返回
	}

	LTNode* prev_node = pos->prev; // 保存待删除节点的前驱节点
	prev_node->next = pos->next; // 将前驱节点的 next 指针指向待删除节点的后继节点
	if (pos->next != NULL) {
    
    
		pos->next->prev = prev_node; // 如果待删除节点有后继节点,则将后继节点的 prev 指针指向前驱节点
	}
	free(pos); // 释放待删除节点的内存
}

This is a C language function used to delete the node at the specified position in the doubly circular linked list with the head node. The parameter of the function is the pointer pos pointing to the specified node, and the function has no return value. The function name is ListErase, which means "delete the linked list".

The implementation of a function usually involves the following steps:

Check whether the node to be deleted is empty, and return directly if it is empty or the head node.
Save the predecessor node pointer of the node to be deleted.
Point the next pointer of the predecessor node to the successor node of the node to be deleted.
If the node to be deleted has a successor node, set the prev pointer of the successor node to the predecessor node.
Release the memory of the node to be deleted.

LTNode* BuyListNode(LTDataType x)
{
    
    
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
    
    
		perror("malloc fail");
		return NULL;
	}
	node->next = NULL;
	node->prev = NULL;
	node->data = x;
	return node;
}

This is a C language function that creates a new doubly linked list node and returns its pointer. The parameter of the function is the data x stored in the node, and the function returns a pointer to the new node. The function name is BuyListNode, which means "buy linked list node", and it can usually be called CreateListNode or NewListNode.

The implementation of a function usually involves the following steps:

Allocate memory space for a new node structure.
Assign the node's data member to the parameter x.
Initializes the node's next and prev member pointers to NULL.
Returns a pointer to the new node.

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
    
    
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}LTNode;


//双向列表的初始化
LTNode* LTInit();
// 双向链表销毁
void ListDestory(LTNode* plist);
// 双向链表打印
void ListPrint(LTNode* plist);
// 双向链表尾插
void ListPushBack(LTNode* plist, LTDataType x);
// 双向链表尾删
void ListPopBack(LTNode* plist);
// 双向链表头插
void ListPushFront(LTNode* plist, LTDataType x);
// 双向链表头删
void ListPopFront(LTNode* plist);
// 双向链表查找
LTNode* ListFind(LTNode* plist, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x);
// 双向链表删除pos位置的结点
void ListErase(LTNode* pos);

LTNode* BuyListNode(LTDataType x)
{
    
    
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
    
    
		perror("malloc fail");
		return NULL;
	}
	node->next = NULL;
	node->prev = NULL;
	node->data = x;
	return node;
}

//双向列表的初始化
LTNode* LTInit()
{
    
    
	LTNode* plist= BuyListNode(-1);
	plist->next = plist;
	plist->prev = plist;
	return plist;
}
// 双向链表销毁
void ListDestory(LTNode* plist) {
    
    
	LTNode* p = plist; // 从头节点开始遍历
	while (p != NULL) {
    
    
		LTNode* tmp = p; // 保存当前节点指针
		p = p->next; // 移动到下一个节点
		free(tmp); // 释放当前节点的内存
	}
	plist = NULL; // 将头结点指针置为 NULL
}

// 双向链表打印
void ListPrint(LTNode* plist)
{
    
    
	assert(plist);
	printf("<=head=>");
	LTNode* cur = plist->next;
	while (cur != plist)
	{
    
    
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}


//判断链表是否为空
bool LTEmpty(LTNode* plist)
{
    
    
	assert(plist);
	return plist->next == plist;
}

// 双向链表尾删
void ListPopBack(LTNode* plist)
{
    
    
	assert(plist);
	assert(!LTEmpty(plist));
	LTNode* tail = plist->prev;
	LTNode* tailPrev = tail->prev;
	tailPrev->next = plist;
	plist->prev = tailPrev;
	free(tail);
	tail = NULL;
}

// 双向链表头插
void ListPushFront(LTNode* plist, LTDataType x)
{
    
    
	assert(plist);
	/*LTNode* newnode = BuyListNode(x);
	newnode->next = plist->next;
	plist->next->prev = newnode;
	plist->next = newnode;
	newnode->prev = plist;*/
	ListInsert(plist->next, x);
}

// 双向链表尾插
void ListPushBack(LTNode* plist, LTDataType x)
{
    
    
	assert(plist);
	/*LTNode* newnode = BuyListNode(x);
	LTNode* tail = plist->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = plist;
	plist->prev = newnode;*/
	ListInsert(plist, x);
}
// 双向链表头删
void ListPopFront(LTNode* plist) {
    
    
	if (plist == NULL) {
    
    
		return; // 如果链表为空,直接返回
	}

	LTNode* p = plist; // 保存头节点指针
	plist = plist->next; // 将下一个节点设置为新的头节点
	if (plist != NULL) {
    
    
		plist->prev = NULL; // 如果新的头节点不为空,则将其前驱节点的指针设置为 NULL
	}
	free(p); // 释放原始头节点的内存
}
// 双向链表查找
LTNode* ListFind(LTNode* plist, LTDataType x) 
{
    
    
	LTNode* p = plist->next; // 从第一个节点开始遍历
	while (p != NULL && p->data != x) {
    
     // 遍历链表,直到找到指定数据或到达链表结尾
		p = p->next; // 移动到下一个节点
	}
	return p; // 返回找到的节点的指针,如果未找到则返回 NULL
}
// 双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x)
{
    
    
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* newnode = BuyListNode(x);
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}
// 双向链表删除pos位置的结点
void ListErase(LTNode* pos) 
{
    
    
	if (pos == NULL || pos->prev == NULL) {
    
    
		return; // 如果待删除节点为 NULL 或者是头结点,则直接返回
	}

	LTNode* prev_node = pos->prev; // 保存待删除节点的前驱节点
	prev_node->next = pos->next; // 将前驱节点的 next 指针指向待删除节点的后继节点
	if (pos->next != NULL) {
    
    
		pos->next->prev = prev_node; // 如果待删除节点有后继节点,则将后继节点的 prev 指针指向前驱节点
	}
	free(pos); // 释放待删除节点的内存
}

Guess you like

Origin blog.csdn.net/weixin_51799303/article/details/131344295