[Data structure] Implementation of the lead bidirectional circular linked list

Table of contents

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

        2. Take the lead in the realization of the bidirectional circular linked list

              1. Create a dynamic head node

              2. Doubly linked list initialization

              3. Print the doubly linked list

              4. Two-way linked list tail plug

              5. Doubly linked list tail deletion

              6. Two-way linked list head plug

              7. Doubly linked list header deletion

              8. Doubly linked list lookup

              9. The doubly linked list inserts x in front of pos

              10. The doubly linked list deletes the node at position pos

              11. Destroy the linked list

              12. The length of the doubly linked list

              13. Empty judgment

        3. Source code

              1、DList.h

              2、DList.c

              3、test.c

        Fourth, the advantages and disadvantages of sequence list and linked list


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

Leading two-way circular linked list: the most complex structure, generally used to store data separately. The linked list data structure used in practice is a two-way circular linked list with the lead. In addition, although the structure is complex, after using the code to realize it, you will find that the structure will bring many advantages, and the implementation is simple. It can be said to be a perfect linked list, which can be forward or backward.

链表的声明
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}LTNode;

2. Take the lead in the realization of the bidirectional circular linked list

 1. Create a dynamic head node

 First create a dynamic node for later use when inserting.

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

 2. Doubly linked list initialization

The leading bidirectional circular linked list has a head node, so a head node is created, and then the front and back pointer fields of the head node point to itself, and the initialization is realized.

LTNode* ListInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

 3. Print the doubly linked list

 Here another pointer cur is needed to traverse the linked list, and it will end when it returns to the head node.

void ListPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

 4. Two-way linked list tail plug

The logic is as shown in the figure below, as long as the figure is drawn, it will be clearer. 

void ListPushBcak(LTNode* phead, LTDataType x)
{
	assert(phead);	
    LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;

	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

 5. Doubly linked list tail deletion

The logic is as follows, and the tail deletion can be represented according to the figure below.

void ListPopBcak(LTNode* phead)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	tailPrev->next = phead;
	phead->prev = tailPrev;
	free(tail);
}

 6. Two-way linked list head plug

 The logic is as follows. According to the figure below, the plug-in can be represented.

void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = BuyListNode(x);
	LTNode* first = phead->next;
	//phead newnode first
	//顺序无关
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;
}

 7. Doubly linked list header deletion

The logic is as follows: 

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);//是否为空
	LTNode* first = phead->next;
	LTNode* second = first->next;
	free(first);
	phead->next = second;
	second->prev = phead;
}

 8. Doubly linked list lookup

Start matching one by one from the back node of the head node of the linked list until a node with the same value is found and return. If the search node goes all the way to the head node, it means that there is no such node, then return NULL.

LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

 9. The doubly linked list inserts x in front of pos

The logic is as follows: 

void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* newnode = BuyListNode(x);
	//prev  newnode  pos
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

 10. The doubly linked list deletes the node at position pos

The logic is as follows: 

void ListErase(LTNode* pos)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	free(pos);
	prev->next = next;
	next->prev = prev;
}

 11. Destroy the linked list

 The doubly linked list is the same as the singly linked list, which is traversed one by one and destroyed one by one.

void ListDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

 12. The length of the doubly linked list

size_t ListSize(LTNode* phead)
{
	assert(phead);
	size_t size = 0;
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

 13. Empty judgment

bool ListEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}

3. Source code

 1、DList.h

#pragma once

#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* BuyListNode(LTDataType x);
//初始化
LTNode* ListInit();

//打印链表
void ListPrint(LTNode* phead);

//双向链表尾插
void ListPushBcak(LTNode* phead, LTDataType x);
//双向链表尾删
void ListPopBcak(LTNode* phead);

//双向链表头插
void ListPushFront(LTNode* phead, LTDataType x);
//双向链表头删
void ListPopFront(LTNode* phead);

//双向链表查找
LTNode* ListFind(LTNode* phead, LTDataType x);

//双向链表在pos的前面进行插入
void ListInsert(LTNode* pos, LTDataType x);
//双向链表删除pos位置的结点
void ListErase(LTNode* pos);

//判空
bool ListEmpty(LTNode* phead);

size_t ListSize(LTNode* phead);

//销毁链表
void ListDestory(LTNode* phead);

 2、DList.c

#include "DList.h"

//创建一个动态头结点
LTNode* BuyListNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
}
//初始化
LTNode* ListInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

//打印链表
void ListPrint(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
//双向链表尾插
void ListPushBcak(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;

	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}
//双向链表尾删
void ListPopBcak(LTNode* phead)
{
	assert(phead);
	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	tailPrev->next = phead;
	phead->prev = tailPrev;
	free(tail);
}

//双向链表头插
void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	LTNode* first = phead->next;
	//phead newnode first
	//顺序无关
	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = first;
	first->prev = newnode;
}
//双向链表头删
void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead);//是否为空
	LTNode* first = phead->next;
	LTNode* second = first->next;
	free(first);
	phead->next = second;
	second->prev = phead;
}

//双向链表查找
LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

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

//双向链表删除pos位置的结点
void ListErase(LTNode* pos)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	free(pos);
	prev->next = next;
	next->prev = prev;
}

//判空
bool ListEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}

size_t ListSize(LTNode* phead)
{
	assert(phead);
	size_t size = 0;
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

//销毁链表
void ListDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

 3、test.c

void TestList1()
{
	LTNode* phead = ListInit();
	ListPushBcak(phead, 1);
	ListPushBcak(phead, 2);
	ListPushBcak(phead, 3);
	ListPushBcak(phead, 4);
	ListPushBcak(phead, 5);
	ListPrint(phead);

	ListPopBcak(phead);
	ListPrint(phead);
	ListPopBcak(phead);
	ListPrint(phead);

	ListPushFront(phead, 10);
	ListPushFront(phead, 20);
	ListPushFront(phead, 30);
	ListPrint(phead);

	ListPopFront(phead);
	ListPrint(phead);
	LTNode* pos = ListFind(phead, 2);
	if (pos)
	{
		pos->data *= 100;
	}
	ListPrint(phead);
	ListDestory(phead);
	phead = NULL;
}
int main()
{
	TestList1();
	return 0;
}

Fourth, the advantages and disadvantages of sequence list and linked list

1. Advantages of the sequence table: tail insertion and tail deletion are convenient, and random access to subscripts is fast. High cache utilization.

2. Disadvantages of the sequence table: When the space is insufficient, it needs to be expanded (expansion needs to pay a corresponding price), and inserting and deleting data needs to move the data.

3. Linked list advantages: easy to insert and delete, apply to release small node memory on demand.
4. Disadvantages of linked list: subscripts cannot be accessed randomly. Cache utilization is low.

difference sequence table linked list
storage space must be physically continuous Logically continuous, but not necessarily physically continuous
random access supports O(1) Not supported: O(N)
Insert or delete elements at any position May need to move elements, low efficiency O(N) Just modify the pointer to point to
insert Dynamic sequence table, when the space is not enough, it needs to be expanded no concept of capacity
Application Scenario Efficient storage of elements + frequent access Frequent insertion and deletion at any position
cache utilization high Low

 If there are deficiencies in this article, you are welcome to comment below, and I will correct it as soon as possible.

  Old irons, remember to like and pay attention!!!

Guess you like

Origin blog.csdn.net/m0_63198468/article/details/128526548