Data structure – headed two-way circular linked list

Hello, I’m Yui.

Foreword

Speaking of linked lists, we talked about singly linked lists before, but there are more than one types of linked lists, if you want to classify them. Linked lists can be divided into headed or non-headed, one-way or two-way, cyclic or non-cyclic, which means that there should be a total of 8 structures of linked lists. The linked list we talked about last time is a one-way non-cyclic linked list without a leader. It is the simplest structure among linked lists.



Let’s briefly explain the two types of linked lists:
1.Headless one-way non-cyclic linked list: The structure is simple and is generally not used to store data alone. In practice, it is more often used as other data structures.
Structured substructures, such as hash buckets, graph adjacency lists, etc. In addition, this structure appears a lot in written interviews.
2. Headed two-way circular linked list: The structure is the most complex and is generally used to store data separately. The linked list data structures used in practice are all
It is a leading two-way circular linked list. In addition, although this structure is complex, after using the code to implement it, you will find that the structure will bring many advantages, and the implementation will be simpler. We will know it later when we implement the code.

Implementation of linked list

Having said so much before, the most important thing is to be able to implement the linked list yourself. The following is the implementation of the teaching.

Create different files

Create 3 files, the functions to be implemented are function declaration, function definition, and test. The purpose is to facilitate subsequent additions, deletions, checking and modifications.

Creation of structure

prev represents the previous node and next represents the next node.

Declaration of function

It is similar to the function declaration of a singly linked list, including head deletion, head insertion, tail insertion, tail deletion, printing, etc…
We finish writing the function declarations first, and then implement them one by one later.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int LTDataType;
typedef struct ListNode
{<!-- -->
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
}ListNode;

//Create the head node of the returned linked list.
ListNode* ListCreate();
// Destroy doubly linked list
void ListDestory(ListNode* pHead);
//Print doubly linked list
void ListPrint(ListNode* pHead);
//Insert at the end of the doubly linked list
void ListPushBack(ListNode* pHead, LTDataType x);
// Delete the end of the doubly linked list
void ListPopBack(ListNode* pHead);
// Insert the head of the two-way linked list
void ListPushFront(ListNode* pHead, LTDataType x);
// Delete the header of the doubly linked list
void ListPopFront(ListNode* pHead);
//Doubly linked list search
ListNode* ListFind(ListNode* pHead, LTDataType x);
// Doubly linked list is inserted in front of pos
void ListInsert(ListNode* pos, LTDataType x);
// Delete the node at pos position in the doubly linked list
void ListErase(ListNode* pos);

After writing the function declaration, the next step is to implement the function.

Function implementation

The implementation of the function is written in the list.c file
Let’s first talk about the creation of the head node. The head node is the head of the linked list and does not store valid data.

Creation of head node

//Create the head node of the returned linked list.
ListNode* ListCreate()
{<!-- -->
ListNode* head = (ListNode*)malloc(sizeof(ListNode));
head->data = -1;//You can store any data, and the data of the head node will not be used in subsequent operations.
head->prev = head;
head->next = head;//Both prev and next should point to themselves to achieve looping.
return head;
}

After writing the creation of the head node, we also need to write a printing function to make subsequent operations more intuitive.

Implementation of printing function

//Double linked list printing
void ListPrint(ListNode* pHead)
{<!-- -->
ListNode* cur = pHead->next;
printf("phead");
while (cur != pHead)
{<!-- -->
printf("%d<=>", cur->data);
cur = cur->next;
}
printf("phead\\
");
}

cur!=pHead, the purpose is not to print the head node.
Next we have to write an insertion function to observe intuitively. So we next write the tail interpolation function.

Implementation of tail insertion function


Drawing explanation

//Tail insert in doubly linked list
void ListPushBack(ListNode* pHead, LTDataType x)
{<!-- -->
assert(pHead);
ListNode* newnode = CreatNode(x);
ListNode* cur = pHead;
ListNode* tail = pHead->prev;
tail->next = newnode;
newnode->prev = tail;
cur->prev = newnode;
newnode->next = cur;
}

After writing the tail insertion function, we have to write a new node creation function

ListNode* CreatNode(LTDataType x)
{<!-- -->
ListNode* newnode = (ListNode* )malloc(sizeof(ListNode));
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}

Oak, after finishing writing, start printing the test.

If it seems to be successful, it means there is no problem. If there is any problem, check again later.

Implementation of header plug-in function

After writing the tail insertion, of course it is time to insert the head insertion.

Drawing explanation

//Two-way linked list head insertion
void ListPushFront(ListNode* pHead, LTDataType x)
{<!-- -->
assert(pHead);
ListNode* newnode = CreatNode(x);
ListNode* cur = pHead;
ListNode* next = pHead->next;
cur->next = newnode;
newnode->prev = cur;
newnode->next = next;
next->prev = newnode;
}

Next, test it and see.

Still no problem.

Implementation of tail deletion function

Remember to free after deletion

//Delete the tail of the doubly linked list
void ListPopBack(ListNode* pHead)
{<!-- -->
assert(pHead->next != pHead);//When there is only the head node, an error is reported
ListNode* cur = pHead;
ListNode* tail = pHead->prev;
cur->prev = tail->prev;
tail->prev->next = cur;
free(tail);
tail = NULL;
}

test
Let’s delete some first, then delete them all, then delete one more and test them separately.



We can also see from these three pictures that there is no problem with the code. (Here I quietly changed the code of the printing function, do you see it?)

Implementation of header deletion function

Remember to be free

//Delete the header of the doubly linked list
void ListPopFront(ListNode* pHead)
{<!-- -->
assert(pHead->next != pHead);
ListNode* first = pHead->next;
pHead->next = first->next;
first->next->prev = pHead;
free(first);
first = NULL;
}

Test again below to see if there is any problem.


There is no problem either.

Implementation of destruction function

Why was it destroyed? Of course, I want to treat the remaining additions, deletions, and modifications at the specified location as homework.

//double linked list destruction
void ListDestory(ListNode* pHead)
{<!-- -->
ListNode* cur = pHead->next;
while (cur != pHead)
{<!-- -->
ListNode* next = cur->next;
free(cur);
cur = NULL;
cur = next;
}
//Finally free pHead
free(pHead);
pHead = NULL;
}

That’s it.
Below we post all the code (including the search function and the addition, deletion, and modification functions at the specified location)

Code

list.h

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int LTDataType;
typedef struct ListNode
{<!-- -->
LTDataType data;
struct ListNode* prev;
struct ListNode* next;
}ListNode;

//Create the head node of the returned linked list.
ListNode* ListCreate();
// Destroy doubly linked list
void ListDestory(ListNode* pHead);
//Print doubly linked list
void ListPrint(ListNode* pHead);
//Insert at the end of the doubly linked list
void ListPushBack(ListNode* pHead, LTDataType x);
// Delete the end of the doubly linked list
void ListPopBack(ListNode* pHead);
// Insert the head of the two-way linked list
void ListPushFront(ListNode* pHead, LTDataType x);
// Delete the header of the doubly linked list
void ListPopFront(ListNode* pHead);
//Doubly linked list search
ListNode* ListFind(ListNode* pHead, LTDataType x);
// Doubly linked list is inserted in front of pos
void ListInsert(ListNode* pos, LTDataType x);
// Delete the node at pos position in the doubly linked list
void ListErase(ListNode* pos);

list.c

#define _CRT_SECURE_NO_WARNINGS
#include "list.h"
ListNode* CreatNode(LTDataType x)
{<!-- -->
ListNode* newnode = (ListNode* )malloc(sizeof(ListNode));
newnode->data = x;
newnode->next = NULL;
newnode->prev = NULL;
return newnode;
}
//Create the head node of the returned linked list.
ListNode* ListCreate()
{
ListNode* head = (ListNode*)malloc(sizeof(ListNode));
head->data = -1;//You can store any data, and the data of the head node will not be used in subsequent operations.
head->prev = head;
head->next = head;//prev and next must point to themselves to achieve looping.
return head;
}
//Print doubly linked list
void ListPrint(ListNode* pHead)
{
ListNode* cur = pHead->next;
printf("phead<=>");
while (cur != pHead)
{
printf("%d<=>", cur->data);
cur = cur->next;
}
printf("phead\\
");
}
//Insert at the end of the doubly linked list
void ListPushBack(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = CreatNode(x);
ListNode* cur = pHead;
ListNode* tail = pHead->prev;
tail->next = newnode;
newnode->prev = tail;
cur->prev = newnode;
newnode->next = cur;
}
// Insert the head of the two-way linked list
void ListPushFront(ListNode* pHead, LTDataType x)
{
assert(pHead);
ListNode* newnode = CreatNode(x);
ListNode* cur = pHead;
ListNode* next = pHead->next;
cur->next = newnode;
newnode->prev = cur;
newnode->next = next;
next->prev = newnode;
}
// Delete the end of the doubly linked list
void ListPopBack(ListNode* pHead)
{
assert(pHead->next != pHead);//An error is reported when there is only the head node
ListNode* cur = pHead;
ListNode* tail = pHead->prev;
cur->prev = tail->prev;
tail->prev->next = cur;
free(tail);
tail = NULL;
}
// Delete the header of the doubly linked list
void ListPopFront(ListNode* pHead)
{
assert(pHead->next != pHead);
ListNode* first = pHead->next;
pHead->next = first->next;
first->next->prev = pHead;
free(first);
first = NULL;
}
// Destroy doubly linked list
void ListDestory(ListNode* pHead)
{
ListNode* cur = pHead->next;
while (cur != pHead)
{
ListNode* next = cur->next;
free(cur);
cur = NULL;
cur = next;
}
//Finally free pHead
free(pHead);
pHead = NULL;
}
//Doubly linked list search
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
ListNode* cur = pHead->next;
if (x == -1)
{
return NULL;
}
while (cur != pHead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
// Doubly linked list is inserted in front of pos
void ListInsert(ListNode* pos, LTDataType x)
{
ListNode* newnode = CreateNode(x);
ListNode* prev = pos->prev;
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;

}
// Delete the node at pos position in the doubly linked list
void ListErase(ListNode* pos)
{
ListNode* prev = pos->prev;
ListNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
pos = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS
#include "list.h"
void test1()
{<!-- -->
ListNode* head = ListCreate();//Creation of head node
ListPushBack(head, 1);
ListPushBack(head, 2);
ListPushBack(head, 3);
ListPushBack(head, 4);
ListPrint(head);
ListPushFront(head, 5);
ListPushFront(head, 6);
ListPushFront(head, 7);
ListPrint(head);
}void test2()
{<!-- -->
ListNode* head = ListCreate();//Creation of head node
ListPushBack(head, 1);
ListPushBack(head, 2);
ListPushBack(head, 3);
ListPushBack(head, 4);
ListPrint(head);
ListPopFront(head);
ListPopFront(head);
ListPopFront(head);
ListPopFront(head);
ListPopFront(head);

ListPrint(head);
}
int main()
{<!-- -->
test2();

return 0;
}

over