Data structure + head node doubly circular linked list + (C language)

Explain the basic functions of the headed doubly circular linked list 

1. Structure definition

 a. The first is to define the structure, the first line uses typedef to rename (to facilitate subsequent changes), and then the definition of the content in the structure First of all, a data field is used to store data, because it is a doubly circular linked list, it must be defined Two pointers, one is _next pointing to the back, and the other is the _prev pointer that points to the front. Because the typedef is added in front of the structure definition, the last variable ListNode is to replace the role of struct ListNode.

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

2. Create a node

a. We do not use secondary pointers, so set the return value of the function to listnode* type and then return its address after creating a node. First, we create a pointer variable to store the requested space address, and then perform space After applying, it is to let its front and back pointers point to the null respectively for later operation, the passed parameter x is the value of the data field, and finally assign it directly. Then return its address to facilitate subsequent operations.

ListNode* ListCreate(LTDataType x) {
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->_next = NULL;
	node->_prev = NULL;
	node->_data = x;
	return node;
}

3. Initialization

a. Initialization is used to improve the head node, because we are a headed doubly circular linked list, so this head node cannot carry valid data, we set it to 0, because it is a doubly circular linked list, so we need to insert the data. Or the nosebleed of tail-inserted data processes the head node so that its front and rear pointers point to itself, so that it can be completed when inserting data later. (This initialization process is only for the head node).

void ListNodeInit(ListNode* phead) {
	phead->_next = phead;
	phead->_prev = phead;
}

4. Doubly linked list printing

a. We want to print the node must start printing from the end of the head node, so create a cur pointer to store the first data node after the head node, and use a while loop to traverse and print, because our The judgment condition is the next of cur, so the last data node cannot be printed, so when the loop ends, we manually print the last node. (Of course, if the judgment condition is processed to a certain extent, it can also be printed all in while, It will not be described in detail here).

void ListPrint(ListNode* pHead) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	while (cur->_next != pHead) {
		printf("%d--->", cur->_data);
		cur = cur->_next;
	}
	printf("%d", cur->_data);
	printf("\n");
}

5. Doubly linked list tail insertion

a. Before inserting, judge whether the incoming parameters are legal or not. If it is not empty, then perform subsequent operations, first define a pointer variable cur to store the first address of the space created by the previous node creation function, and secondly because it is the tail Insert so we need to find the tail node, our linked list is a doubly circular linked list, so we only need to take the previous node of the head node as the tail node, and then perform pointer conversion to insert the new node at the end. (As shown in the figure )

 

void ListPushBack(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* tail = pHead->_prev;
	tail->_next = cur;
	pHead->_prev = cur;
	cur->_next = pHead;
	cur->_prev = tail;
}

6. Doubly linked list tail deletion

a. Before deleting, judge whether the incoming parameter is legal. If it is not empty, follow-up operations are performed. Similar to tail insertion, we must first find the tail node and record it as tail, then convert the pointer, and release it after deletion. Space. (See the picture for details)

void ListPopBack(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* tail = pHead->_prev;
	pHead->_prev = pHead->_prev->_prev;
	pHead->_prev->_next = pHead;
	free(tail);
}

7. Doubly linked list header

a.. Before doing the header insertion, first judge whether the incoming parameters are legal. If it is not empty, in the subsequent operation, first define a pointer variable cur to store the first address of the space created by the previous node creation function, and secondly because It is a head plug, so we have to find the first data node, which is the next of the head, and then perform pointer conversion to insert. (As shown in the figure)

void ListPushFront(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* Next = pHead->_next;
	pHead->_next = cur;
	cur->_next = Next;
	Next->_prev = cur;
	cur->_prev = pHead;
}

8. Doubly linked list header deletion

a. Before deleting, first judge whether the incoming parameters are legal. If it is not empty, follow-up operations are performed. Similar to head insertion, first find the first data node cur, which is the next of the head, followed by the second one. The data node, that is, the next of the next head of the head, then performs related pointer operations, and finally releases the cur. (As shown in the figure)

void ListPopFront(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	ListNode* Next = pHead->_next->_next;
	pHead->_next = Next;
	Next->_prev = pHead;
	free(cur);
}

9. Doubly linked list lookup

a. When we define the function here, set its return value as listnode* type, pass the corresponding parameter x, use the while loop to traverse the entire linked list to find the node whose data field is equal to x and return the address of this node, if not found, return null.

ListNode* ListFind(ListNode* pHead, LTDataType x) {
	if (NULL == pHead || pHead->_next == pHead) {
		return NULL;
	}
	ListNode* temp = pHead->_next;
	while (temp != pHead) {
		if (temp->_data == x) {
			return temp;
		}
		temp = temp->_next;
	}
	return NULL;
}

10. Doubly linked list is inserted in front of pos

a. Similar to the head plug, before performing the operation, judge whether the position can be found and can be found for subsequent operations. Here, if we use the above search function, find the position you want to insert and then insert, and then find this The previous node of the position is recorded as prv, and then the newly created node uses cur to store its address, and then the pointer is transformed, the next of prv points to cur, the prev of the node pos of this position points to cur, and then there is cur The prev points to prv, the next of cur points to pos, and the insertion is completed.

void ListInsert(ListNode* pos, LTDataType x) {
	if (NULL == pos) {
		printf("插入数据的位置不存在!\n");
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* prv = pos->_prev;
	prv->_next = cur;
	pos->_prev = cur;
	cur->_prev = prv;
	cur->_next = pos;
}

11. Doubly linked list delete the node at pos position

a. Before performing the operation, judge whether the position can be found and can be found for subsequent operations. First, call the search function to find the position to be inserted and record it as pos, then take the previous one and record it as prv, and record the latter one as Next , followed by the operation of the pointer, the next one of prv points to Next, the previous one of Next points to prv, and finally the space of the node at the pos position is released. Successfully deleted.

void ListErase(ListNode* pos) {
	if (NULL == pos) {
		printf("删除数据的位置不存在!\n");
		return;
	}
	ListNode* prv = pos->_prev;
	ListNode* Next = pos->_next;
	prv->_next = Next;
	Next->_prev = prv;
	free(pos);
}

12. Destroy the linked list

a. After all our operations are completed, the linked list needs to be destroyed. This is very simple, we use the while loop to call the head delete or tail delete function to delete all data nodes except the head node and release the space (we Here we use head deletion), it should be noted here that when we pass parameters, we pass a secondary pointer, because after I release the space of the head node, the address stored by the head pointer has no space, which means that the head pointer becomes For wild pointers, we need to reassign them to NULL to make them not wild pointers, but to really change the value of the pointer, when passing parameters, we must pass the address of the pointer before it can be modified, so we use a secondary pointer , that is, the address of the passed pointer.

void ListDestory(ListNode** pHead) {
	if (NULL == (*pHead)) {
		return;
	}
	while ((*pHead)->_next!=*pHead) {
		ListPopFront(*pHead);
	}
	free(*pHead);
	(*pHead) = NULL;
}

The last is a series of tests and test screenshots and all the code:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
//结构体定义
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;
//创建结点
ListNode* ListCreate(LTDataType x) {
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	node->_next = NULL;
	node->_prev = NULL;
	node->_data = x;
	return node;
}
//初始化
void ListNodeInit(ListNode* phead) {
	phead->_next = phead;
	phead->_prev = phead;
}
// 双向链表销毁
void ListDestory(ListNode** pHead) {
	if (NULL == (*pHead)) {
		return;
	}
	while ((*pHead)->_next!=*pHead) {
		ListPopFront(*pHead);
	}
	free(*pHead);
	(*pHead) = NULL;
}
// 双向链表打印
void ListPrint(ListNode* pHead) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	while (cur->_next != pHead) {
		printf("%d--->", cur->_data);
		cur = cur->_next;
	}
	printf("%d", cur->_data);
	printf("\n");
}
// 双向循环链表尾插
void ListPushBack(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* tail = pHead->_prev;
	tail->_next = cur;
	pHead->_prev = cur;
	cur->_next = pHead;
	cur->_prev = tail;
}
// 双向链表尾删
void ListPopBack(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* tail = pHead->_prev;
	pHead->_prev = pHead->_prev->_prev;
	pHead->_prev->_next = pHead;
	free(tail);
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x) {
	if (NULL == pHead) {
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* Next = pHead->_next;
	pHead->_next = cur;
	cur->_next = Next;
	Next->_prev = cur;
	cur->_prev = pHead;
}
// 双向链表头删
void ListPopFront(ListNode* pHead) {
	if (NULL == pHead || pHead->_next == pHead) {
		return;
	}
	ListNode* cur = pHead->_next;
	ListNode* Next = pHead->_next->_next;
	pHead->_next = Next;
	Next->_prev = pHead;
	free(cur);
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x) {
	if (NULL == pHead || pHead->_next == pHead) {
		return NULL;
	}
	ListNode* temp = pHead->_next;
	while (temp != pHead) {
		if (temp->_data == x) {
			return temp;
		}
		temp = temp->_next;
	}
	return NULL;
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x) {
	if (NULL == pos) {
		printf("插入数据的位置不存在!\n");
		return;
	}
	ListNode* cur = ListCreate(x);
	ListNode* prv = pos->_prev;
	prv->_next = cur;
	pos->_prev = cur;
	cur->_prev = prv;
	cur->_next = pos;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos) {
	if (NULL == pos) {
		printf("删除数据的位置不存在!\n");
		return;
	}
	ListNode* prv = pos->_prev;
	ListNode* Next = pos->_next;
	prv->_next = Next;
	Next->_prev = prv;
	free(pos);
}
//测试函数
void main() {
	ListNode* phead = ListCreate(0);
	ListNodeInit(phead);
	ListPushBack(phead, 10);
	ListPushBack(phead, 11);
	ListPushBack(phead, 12);
	ListPushBack(phead, 13);
	ListPushBack(phead, 14);
	ListPushBack(phead, 15);
	ListPrint(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPopBack(phead);
	ListPrint(phead);
	ListPushFront(phead, 1);
	ListPushFront(phead, 5);
	ListPushFront(phead, 3);
	ListPushFront(phead, 4);
	ListPrint(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPopFront(phead);
	ListPrint(phead);
	ListInsert(ListFind(phead,11), 90);
	ListPrint(phead);
	ListErase(ListFind(phead, 90));
	ListPrint(phead);
	ListDestory(&phead);
}

Test screenshot

 

Guess you like

Origin blog.csdn.net/weixin_49312527/article/details/121582276