Lead two-way circular linked list: the structure is the most complex. It is generally used to store data separately. The linked list data structure used in practice is a two-way circular linked list. In addition, although the structure is complex, you will find that the structure will bring many advantages after code implementation, but the implementation is simple. We will know after code implementation.
This linked list is designed for the shortcomings of one-way linked list.
catalogue
Bidirectional linked list function interface definition
Implementation of function interface
void ListDestroy(struct ListNode* phead)
LTNode* BuyListNode(LTDataType x)
void ListPushBack(struct ListNode* phead, LTDataType x)
void ListPopBack(struct ListNode* phead)
void ListPushFront(struct ListNode* phead, LTDataType x)
void ListPopFront(struct ListNode* phead)
LTNode* ListFind(struct ListNode* phead, LTDataType x)
void ListInsert(struct ListNode* pos, LTDataType x)
void ListErase(struct ListNode* pos)
void ListPrint(struct ListNode* phead)
Complete code of bidirectional circular linked list
Advantages and disadvantages of two-way linked list
Structure creation
typedef int LTDataType; typedef struct ListNode { struct ListNode* next; //Pointer to next struct ListNode* prev; //Pointer to previous LTDataType data; //Store data }LTNode;
Bidirectional linked list function interface definition
//Initialize the node of the sentry position and receive it with the return value LTNode* ListInit(); //Dynamically open up a node LTNode* BuyListNode(LTDataType x); //Destroy the two-way linked list void ListDestroy(struct ListNode* phead); //Tail insertion void ListPushBack(struct ListNode* phead, LTDataType x); //Tail deletion void ListPopBack(struct ListNode* phead); //Head insert void ListPushFront(struct ListNode* phead, LTDataType x); //Header deletion void ListPopFront(struct ListNode* phead); //Finds the specified data and returns the address of the data LTNode* ListFind(struct ListNode* phead, LTDataType x); //Insert a data before pos void ListInsert(struct ListNode* pos, LTDataType x); //Delete node at pos void ListErase(struct ListNode* pos); //Print data of bidirectional linked list void ListPrint(struct ListNode* phead);
Implementation of function interface
LTNode* ListInit()
LTNode* ListInit() { LTNode* phead = (LTNode*)malloc(sizeof(LTNode)); phead->next = phead; //Point to yourself, not empty phead->prev = phead;//Point to yourself, not empty phead->data = 0; return phead; }
Initialize the sentinel node of the two-way linked list
LTNode* ListInit()
{
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));// Dynamically open sentinel node
phead->next = phead; // Point to yourself, not empty
phead->prev = phead;// Point to yourself, not empty
phead->data = 0; // The sentinel node points to a data first
return phead; // Returns the address of the sentinel node. In the test LTNode* plist = ListInit(); Use the return value to receive the value of the phead address. This linked list does not need to change the head node in the process of use, so the function interface only needs value transfer.
}
void ListDestroy(struct ListNode* phead)
void ListDestroy(struct ListNode* phead) { assert(phead); LTNode* cur = phead->next; while (cur != phead) { LTNode* next = cur->next; free(cur); cur = NULL; cur = next; } free(phead); phead = NULL; }
Destruction of two-way linked list
void ListDestroy(struct ListNode* phead)
{
assert(phead);// Determine whether phead is a valid address
LTNode* cur = phead->next; // Cur records the address of the valid data location
While (cur! = phead) / / when cur is equal to phead, the cycle ends. At this time, all nodes except the sentinel position have been closed It's been traversed.
{
LTNode* next = cur->next;// Next record the address of the next node to be deleted in advance
free(cur); // Release node
cur = NULL;
cur = next; //cur becomes a pre recorded (next) address
}
free(phead);// Release the head node of the sentinel bit and the data on the heap.
phead = NULL;// This step has no effect. The change of formal parameters does not affect the arguments. It needs to be reset in test
}
LTNode* BuyListNode(LTDataType x)
LTNode* BuyListNode(LTDataType x) { LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); if (newnode == NULL) { printf("malloc fail\n"); exit(-1); } else { newnode->next = NULL; newnode->prev = NULL; newnode->data = x; return newnode; } }
Dynamically open up a node to record new data
LTNode* BuyListNode(LTDataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));//newnode records the address of the new node
if (newnode == NULL) / / if the development fails, exit the program
{
printf("malloc fail\n");
exit(-1);
}
else
{
newnode->next = NULL; // The new node points to the next node and is initialized to null
newnode->prev = NULL;// The new node points to the previous node and is initialized to null
newnode->data = x;// The new node stores data
return newnode;// Returns the address of the new node
}
}
void ListPushBack(struct ListNode* phead, LTDataType x)
void ListPushBack(struct ListNode* 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; //ListInsert(phead, x); }
Insert data from tail
void ListPushBack(struct ListNode* phead, LTDataType x)
{
assert(phead);// Judge phead as a valid address
LTNode* newnode = BuyListNode(x); // Open a new node, and newnode receives the address of the new node
LTNode* tail = phead->prev; // Tail records the address of the tail node
tail->next = newnode; // Tail points to the next node, which is newnode
newnode->prev = tail;// Newnode points to the previous node and is tail
newnode->next = phead;// Newnode points to the next node, which is the sentinel node
phead->prev = newnode;// The previous node pointed to by the sentinel node is newnode, which is equivalent to at the end Insert a data
//ListInsert(phead, x);
}
void ListPopBack(struct ListNode* phead)
void ListPopBack(struct ListNode* phead) { assert(phead); assert(phead->next != phead); LTNode* tail = phead->prev; LTNode* tailPrev = tail->prev; tailPrev->next = phead; phead->prev = tailPrev; free(tail); tail = NULL; //ListErase(phead->prev); }
Delete the last node in the linked list
void ListPopBack(struct ListNode* phead)
{
assert(phead);
assert(phead->next != phead); // Judge whether there are other nodes in the linked list in addition to the sentinel node to prevent The sentinel node has been deleted
LTNode* tail = phead->prev;// Record the last node in the linked list
LTNode* tailPrev = tail->prev;// Record the previous node of the tail node
tailPrev->next = phead;
phead->prev = tailPrev;
free(tail);// The two nodes tailprev and phead are connected. At this time, release the node at the tail position, which is equivalent to Delete the tail node of the original linked list
tail = NULL;
//ListErase(phead->prev);
}
void ListPushFront(struct ListNode* phead, LTDataType x)
void ListPushFront(struct ListNode* phead, LTDataType x) { assert(phead); LTNode* newnode = BuyListNode(x); LTNode* head = phead->next; phead->next = newnode; newnode->prev = phead; newnode->next = head; head->prev = newnode; //ListInsert(phead->next, x); }
Insert a data before the first node in the linked list
void ListPushFront(struct ListNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
LTNode* head = phead->next;// Head record chain header node
phead->next = newnode;// The sentinel bit points to the next node, which is the new node newnode
newnode->prev = phead;// The new node refers to the forward node, which is the sentinel node
newnode->next = head;// The previous node pointed to by newnode is the head node of the original linked list
head->prev = newnode;// The previous node pointed to by the head node of the original linked list is the new node newnode
//ListInsert(phead->next, x);
}
void ListPopFront(struct ListNode* phead)
void ListPopFront(struct ListNode* phead) { assert(phead); assert(phead->next != phead); //Prevent deleting header nodes LTNode* head = phead->next; LTNode* next = head->next; phead->next = next; next->prev = phead; free(head); head = NULL; //ListErase(phead->next); }
Delete the first node in the linked list (the next node of the sentinel node)
void ListPopFront(struct ListNode* phead)
{
assert(phead);
assert(phead->next != phead); // Judge whether there are other nodes in the linked list in addition to the sentinel node to prevent The sentinel node has been deleted
LTNode* head = phead->next;// Head records the first node
LTNode* next = head->next;// Next records the next node of the header node
phead->next = next;// The next node pointed to by the sentinel node is next
next->prev = phead;// The previous node pointed to by next is the sentinel node
free(head);// The sentinel node is connected with the head node of the original linked list, and the head node is isolated here If you release it, it's equivalent to deleting the header
head = NULL;
//ListErase(phead->next);
}
LTNode* ListFind(struct ListNode* phead, LTDataType x)
LTNode* ListFind(struct ListNode* phead, LTDataType x) { assert(phead); LTNode* cur = phead->next; while (cur != phead) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
Find the specified data in the linked list and return the address
LTNode* ListFind(struct ListNode* phead, LTDataType x)
{
assert(phead);
LTNode* cur = phead->next;// Cur starts from the first node (the next node of phead)
While (cur! = phead) / / when cur equals phead, the loop ends
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;// Cur goes back and forth
}
return NULL;// If it cannot be found, return null
}
void ListInsert(struct ListNode* pos, LTDataType x)
void ListInsert(struct ListNode* pos, LTDataType x) { assert(pos); LTNode* newnode = BuyListNode(x); LTNode* prev = pos->prev; prev->next = newnode; newnode->prev = prev; newnode->next = pos; pos->prev = newnode; }
Insert a data before the pos position
void ListInsert(struct ListNode* pos, LTDataType x)
{
assert(pos);// Judge POS as a valid address
LTNode* newnode = BuyListNode(x);
LTNode* prev = pos->prev;// Prev is used to record the address of the node before the POS location node
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
Note: this insert can replace the tail insert and head insert
Tail insertion: ListInsert(phead, x);
Header insert: listinsert (phead - > next, x);
void ListErase(struct ListNode* pos)
void ListErase(struct ListNode* pos) { assert(pos); assert(pos->next != pos);//pos cannot be phead LTNode* prev = pos->prev; LTNode* next = pos->next; prev->next = next; next->prev = prev; free(pos); pos = NULL; }
Delete pos node
void ListErase(struct ListNode* pos)
{
assert(pos);
assert(pos->next != pos);// Judge that POS cannot be phead
LTNode* prev = pos->prev;// It is used to record the address of the previous node of POS
LTNode* next = pos->next;// It is used to record the address of the next node of POS
prev->next = next;
next->prev = prev;
//At this time, the prev and next nodes have been connected, and the pos node has been isolated
free(pos); Release POS node
pos = NULL;
}
void ListPrint(struct ListNode* phead)
void ListPrint(struct ListNode* phead) { assert(phead); LTNode* cur = phead->next; //Start from the next node of the ab initio node while (cur != phead) { printf("%d ", cur->data); cur = cur->next; } printf("NULL\n"); }
Print data in linked list
void ListPrint(struct ListNode* phead)
{
assert(phead);
LTNode* cur = phead->next; // Start from the next node of the ab initio node
While (cur! = phead) / / when cur equals phead, the loop ends. At this time, all nodes with valid data have been traversed Again
{
printf("%d ", cur->data);
cur = cur->next; // Cur keeps pointing to the next node
}
printf("NULL\n");
}
Complete code of bidirectional circular linked list
List.h
#define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include<stdio.h> #include<assert.h> #include<stdlib.h> typedef int LTDataType; typedef struct ListNode { struct ListNode* next; struct ListNode* prev; LTDataType data; }LTNode; //Initialize the node of the sentry position and receive it with the return value LTNode* ListInit(); //Dynamically open up a node LTNode* BuyListNode(LTDataType x); //Destroy the two-way linked list void ListDestroy(struct ListNode* phead); //Tail insertion void ListPushBack(struct ListNode* phead, LTDataType x); //Tail deletion void ListPopBack(struct ListNode* phead); //Head insert void ListPushFront(struct ListNode* phead, LTDataType x); //Header deletion void ListPopFront(struct ListNode* phead); //Finds the specified data and returns the address of the data LTNode* ListFind(struct ListNode* phead, LTDataType x); //Insert a data before pos void ListInsert(struct ListNode* pos, LTDataType x); //Delete node at pos void ListErase(struct ListNode* pos); //Print data of bidirectional linked list void ListPrint(struct ListNode* phead);
List.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"List.h" LTNode* ListInit() { LTNode* phead = (LTNode*)malloc(sizeof(LTNode)); phead->next = phead; //Point to yourself, not empty phead->prev = phead;//Point to yourself, not empty phead->data = 0; return phead; } void ListDestroy(struct ListNode* phead) { assert(phead); LTNode* cur = phead->next; while (cur != phead) { LTNode* next = cur->next; free(cur); cur = NULL; cur = next; } free(phead); phead = NULL; } LTNode* BuyListNode(LTDataType x) { LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); if (newnode == NULL) { printf("malloc fail\n"); exit(-1); } else { newnode->next = NULL; newnode->prev = NULL; newnode->data = x; return newnode; } } void ListPushBack(struct ListNode* 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; //ListInsert(phead, x); } void ListPopBack(struct ListNode* phead) { assert(phead); assert(phead->next != phead); LTNode* tail = phead->prev; LTNode* tailPrev = tail->prev; tailPrev->next = phead; phead->prev = tailPrev; free(tail); tail = NULL; //ListErase(phead->prev); } void ListPushFront(struct ListNode* phead, LTDataType x) { assert(phead); LTNode* newnode = BuyListNode(x); LTNode* head = phead->next; phead->next = newnode; newnode->prev = phead; newnode->next = head; head->prev = newnode; //ListInsert(phead->next, x); } void ListPopFront(struct ListNode* phead) { assert(phead); assert(phead->next != phead); //Prevent deleting header nodes LTNode* head = phead->next; LTNode* next = head->next; phead->next = next; next->prev = phead; free(head); head = NULL; //ListErase(phead->next); } LTNode* ListFind(struct ListNode* phead, LTDataType x) { assert(phead); LTNode* cur = phead->next; while (cur != phead) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; } void ListInsert(struct ListNode* pos, LTDataType x) { assert(pos); LTNode* newnode = BuyListNode(x); LTNode* prev = pos->prev; prev->next = newnode; newnode->prev = prev; newnode->next = pos; pos->prev = newnode; } void ListErase(struct ListNode* pos) { assert(pos); assert(pos->next != pos);//pos cannot be phead LTNode* prev = pos->prev; LTNode* next = pos->next; prev->next = next; next->prev = prev; free(pos); pos = NULL; } void ListPrint(struct ListNode* phead) { assert(phead); LTNode* cur = phead->next; //Start from the next node of the ab initio node while (cur != phead) { printf("%d ", cur->data); cur = cur->next; } printf("NULL\n"); }
Code test.c
#define _CRT_SECURE_NO_WARNINGS 1 #include"List.h" void Test1() { LTNode* plist = ListInit(); //Receive with return value ListPushBack(plist, 1);//pass by value ListPushBack(plist, 2); ListPushBack(plist, 3); ListPushBack(plist, 4); ListPopBack(plist); ListPushFront(plist, 10); ListPushFront(plist, 20); ListPushFront(plist, 30); ListPushFront(plist, 40); ListPushFront(plist, 40); ListPushFront(plist, 40); ListPopFront(plist); LTNode* pos = ListFind(plist, 20); if (pos != NULL) { ListInsert(pos, 200); } pos = ListFind(plist, 30); if (pos != NULL) { ListErase(pos); } ListPrint(plist); ListDestroy(plist); plist = NULL; } int main() { Test1(); return 0; }
Test results:
Advantages and disadvantages of two-way linked list
advantage:
1. High insertion efficiency at any position
2. Apply for space on demand, there is no waste of space
Disadvantages:
1. Random access (subscript access) is not supported, which means that some sorting and binary search are not applicable to this structure
2. The linked list stores a value and the link pointer at the same time, which also has a certain consumption
3. cpu cache hit rate is lower (compared with sequential table)