[data structure] handwritten double linked list [pure C language version]

Abstract: this chapter mainly describes the implementation of circular double linked list

Introduction to this chapter

   In this chapter, I will not describe the implementation logic of the code in detail. If you have read the previous articles or written your own single linked list, you will grasp the implementation of circular double linked list in this article; If you are not clear about some of the logic, you can draw your own pictures to understand. Here I will only draw two pictures as a demonstration. Others can draw their own pictures. Drawing is really important!

1. Logical structure of circular double linked list

   Here, I take the circular double linked list of the leading node as an example:

   Each node has a data field and two pointer fields. Prev points to the previous element of the node and next points to the next node of the node. The connection between each two nodes is bidirectional, so we can find the direct precursor and direct successor of the node through any node; It is worth noting that the prev of the head node points to the tail node, and the next field of the tail node points to the head node, which forms a circular linked list.

2. Code implementation of circular double linked list

2.1 define the storage structure of circular double linked list

// Defines the type of linked list data field
typedef int LTDataType;

// Define linked list type
typedef struct ListNode
{
	struct ListNode* prev; // Point to the previous node, that is, point to the direct precursor node
	struct ListNode* next; // Point to the next node, that is, point to the direct successor node
	LTDataType data; // Data domain
} ListNode;

2.2 creating nodes

// New node
ListNode* BuyListNode(LTDataType* data)
{
	if (!data)
	{
		printf("Data field cannot be empty\n");
		return NULL;
	}

	// Create a new node
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	// Air judgment
	if (!newNode)
	{
		perror("Failed to open up space");
		return NULL;
	}

	// insert data
	newNode->data = *data;
	// next points to null
	newNode->next = NULL;
	// prev points to null
	newNode->prev = NULL;

	return newNode;

}

2.2 initializing header nodes

   When there is only one node in the circular double linked list, that is, the head node, the prev field and the next field of the head node point to themselves.

void ListInit(ListNode** pphead)
{
	int a = 10;
	// Create a new node
	ListNode* newNode = BuyListNode(&a);
	// Point to yourself
	newNode->next = newNode;
	newNode->prev = newNode;
	*pphead = newNode;
}

2.3 calculate the length of the linked list

   When calculating the length of the circular double linked list, we will keep the linked list variable. Then the problem comes. As long as the leading node circular double linked list is not empty, it will always have a direction. What should be our cycle termination condition? Very simple, when the current node is the head node, it means that the double linked list has been traversed.

// Calculate the length of the double linked list, excluding the head node
size_t ListSize(ListNode* phead)
{
	// Record current node
	ListNode* cur = phead->next;
	// Record length
	size_t size = 0;
	// If the current node is not a header node
	while ( cur != phead )
	{
		cur = cur->next;
		size ++;
	}
	return size;
}

2.4 tail interpolation

void ListPushBack(ListNode* phead, LTDataType* data)
{
	assert(phead);

	// Create a new node
	ListNode* newNode = BuyListNode(data);
	// Save tail node
	ListNode* tail = phead->prev;
	// The next of the tail node points to the new node
	tail->next = newNode;
	// The prev of the new node points to the tail node
	newNode->prev = tail;
	// The next of the new node points to the header node
	newNode->next = phead;
	// The prev of the head node points to the new node
	phead->prev = newNode;
	// Complete insertion
}

2.5 tail deletion method

// Tail deletion
void ListPopBack(ListNode* phead)
{
	assert(phead);
	// If the linked list is empty, it will be interrupted
	assert(phead->next != phead);

	// Calculate linked list length
	size_t size = ListSize(phead);
	// If the size is less than 1, there are only header nodes in the linked list
	if (size < 1)
	{
		printf("There are no nodes in the linked list\n");
		exit(-1);
	}

	// Save tail node
	ListNode* tail = phead->prev;
	// Save the previous node of the tail node
	ListNode* tailPrev = tail->prev;

	tailPrev->next = phead;
	phead->prev = tail->prev;
	free(tail);
	tail = NULL;
}

2.6 print linked list

void ListPrint(ListNode* phead)
{
	assert(phead);

	// Record current node
	ListNode* cur = phead->next;
	while ( cur != phead)
	{
		printf("%p\t%d\t%p\t%p\n", cur->prev, cur->data, cur->next, cur);
		cur = cur->next;
	}
}

2.7 head insertion method

// Head insert
void ListPushFront(ListNode* phead, LTDataType* data)
{
	// Create a new node
	ListNode* newNode = BuyListNode(data);
	
	// Method 1: the sequence of the following codes in this method cannot be disordered, otherwise header insertion cannot be realized
	/*newNode->prev = phead;
	newNode->next = phead->next;
	phead->next->prev = newNode;
	phead->next = newNode;*/

	// Method 2: the code order of this method can be changed
	ListNode* pheadNext = phead->next;
	phead->next = newNode;
	newNode->prev = phead;
	pheadNext->prev = newNode;
	newNode->next = pheadNext;

}

2.8 header deletion

// Header deletion
void ListPopFront(ListNode* phead)
{
	// Header node cannot be empty
	assert(phead);
	// Calculate linked list length
	assert(phead->next != phead);

	// Method 1: the following code sequence can be disrupted
	ListNode* target = phead->next;
	ListNode* targetNext = target->next;
	phead->next = targetNext;
	targetNext->prev = phead;
	free(target);

	// Method 2: the following code sequence cannot be disturbed
	/*phead->next = phead->next->next;
	free(phead->next->prev);
	phead->next->prev = phead;*/
}

2.9 finding nodes with specified data

// Find the node with the specified data field and return the pointer of the node
ListNode* ListFind(ListNode* phead, LTDataType* data)
{
	assert(phead);
	assert(phead->next != NULL);

	ListNode* cur = phead->next;

	while (cur != phead)
	{
		if (cur->data == *data) return cur;
		cur = cur->next;
	}

	return NULL;

}

2.10 find nodes at specified locations

// Find the node of the location. The value of pos is [0,size-1]
ListNode* ListFindByPos(ListNode* phead, size_t pos)
{
	assert(phead);
	// Interrupt if there is only a header node
	assert(phead->next != phead);
	// Calculate the length of the current linked list
	size_t size = ListSize(phead);
	assert(pos < size);

	// Used to record nodes with pos position
	ListNode* posNode = phead->next;
	
	for (int i = 0; i < pos; i++)
	{
		posNode = posNode->next;
	}

	return posNode;
}

2.11 insert node before any node

// Insert a new node before any node target
void ListInsert(ListNode* target, LTDataType* data)
{
	assert(target);

	
	ListNode* targetPrev = target->prev;
	// Create a new node
	ListNode* newNode = BuyListNode(data);
	
	targetPrev->next = newNode;
	newNode->prev = targetPrev;
	newNode->next = target;
	target->prev = newNode;
}

2.12 find the specified node

// Find whether there is a specified node in the linked list. The node is returned, but NULL is not returned
ListNode* ListIsExistNode(ListNode* phead, ListNode* target)
{
	assert(phead && target);
	assert(phead->next != phead);

	ListNode* cur = phead->next;
	while (cur && (cur != phead))
	{
		if (cur == target) return cur;
		cur = cur->next;
	}
	return NULL;
}

2.13 delete specified node

// Delete specified node
void ListErase(ListNode* target)
{
	assert(target);

	ListNode* targetPrev = target->prev;
	ListNode* targetNext = target->next;

	targetPrev->next = targetNext;
	targetNext->prev = targetPrev;
	free(target);
}

2.14 empty linked list

// Clear the linked list and keep the header node
void ListClear(ListNode* phead)
{
	assert(phead && phead->next != phead);

	ListNode* cur = phead->next;
	while (cur != phead)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}

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

2.15 release linked list

// Release the linked list, including the header node
void ListDestory(ListNode** pphead)
{
	assert(*pphead);
	// Empty linked list memory
	ListClear(*pphead);
	// Release head
	free(*pphead);
	// Set the linked list to empty
	*pphead = NULL;
}

3. Source code link

Click to view the source code

[document description]

file nameexplain
SList.hType definition and function declaration of linked list
SList.cImplementation of specific functions
test.cTest code

Postscript

My level is limited and mistakes are inevitable. I hope you can correct me.

This is the end of the double linked list. Thank you for reading!!! If the content is helpful to you, remember to give me sanlianya (praise, collection and attention)

All articles in my blog are original. Some articles or cite relevant materials, but the source has been clearly indicated. You can reprint and share at will, but you need to add a link to this article and a copyright description.

Tags: C data structure linked list

Posted on Sat, 06 Nov 2021 19:11:44 -0400 by Cleanselol