Table of contents
The structure of a headed two-way circular linked list
2. Implementation of linked list
2.6 Insert before pos position
3. Complete source code of doubly linked list
Preface
In the last issue, we introduced singly linked lists and did some exercises. Using singly linked lists in some questions would be very cumbersome. Because a singly linked list can only go forward, not backwards, such as palindromes and inversions. In this issue we will learn to take the lead in a two-way circular linked list.
1. Doubly linked list
The structure of a headed two-way circular linked list
Features: The leading two-way circular linked list structure is the most complex and is generally used to store data separately. Although the 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.
2. Implementation of linked list
2.1 Initialization
LTNode* LTInit()
{
LTNode* phead = CreateLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
2.2 Tail plug
When inserting at the end of a linked list with a sentinel bit, there is no need to judge whether there is a node. The insertion in both cases is the same, and there is no need to pass a secondary pointer.
void LTPushBack(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* newnode = CreateLTNode(x);
phead->prev->next = newnode;
newnode->prev = phead->prev;
newnode->next = phead;
phead->prev = newnode;
}
2.3 Tail deletion
When deleting the tail, we use assert(phead->next != phead); to determine whether the linked list has nodes. At the same time, this code is universal, and there is no need to consider the situation of one remaining node separately.
void LTPopBack(LTNode* phead)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
free(tail);
phead->prev = tailprev;
tailprev->next = phead;
}
2.4 Head plug
The most important thing about head deletion is the order of assignment. If the order is wrong, subsequent nodes will not be found, causing memory leaks. A linked list with a sentinel bit does not need to pass a secondary pointer, because what is changed is the variable of the structure.
void LTPushFront(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* newnode = CreateLTNode(x);
newnode->next = phead->next;
phead->next->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
}
2.5 Header deletion
We can define a few more pointers to save the addresses of subsequent nodes, so that the nodes will not be lost and there is no need to consider the order of assignment, which will be more convenient.
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* tail = phead->next;
LTNode* next = tail->next;
phead->next = next;
next->prev = phead;
free(tail);
tail = NULL;
}
2.6 Insert before pos position
void LTInsert(LTNode* pos, LTDateType x)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* newnode = CreateLTNode(x);
posprev->next = newnode;
newnode->prev = posprev;
newnode->next = pos;
pos->prev = newnode;
}
2.7 Delete pos position
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* posnext = pos->next;
posprev->next = posnext;
posnext->prev = posprev;
}
3. Complete source code of doubly linked list
List.h
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int LTDateType;
typedef struct ListNode
{
LTDateType val;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
LTNode* LTInit();
void LTPrint(LTNode* phead);
void LTPushBack(LTNode* phead, LTDateType x);
void LTPushFront(LTNode* phead, LTDateType x);
void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);
LTNode* LTFind(LTNode* phead, LTDateType x);
void LTInsert(LTNode* pos, LTDateType x);
void LTErase(LTNode* pos);
void LTDestroy(LTNode* phead);
List.c
#include"List.h"
LTNode* CreateLTNode(LTDateType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
perror("malloc fail");
exit(-1);
}
newnode->val = x;
newnode->next = NULL;
newnode->prev = NULL;
}
LTNode* LTInit()
{
LTNode* phead = CreateLTNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
void LTPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
printf("<=>哨兵位<=>");
while (cur != phead)
{
printf("%d<=>", cur->val);
cur = cur->next;
}
printf("\n");
}
void LTPushBack(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* newnode = CreateLTNode(x);
phead->prev->next = newnode;
newnode->prev = phead->prev;
newnode->next = phead;
phead->prev = newnode;
}
void LTPushFront(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* newnode = CreateLTNode(x);
newnode->next = phead->next;
phead->next->prev = newnode;
phead->next = newnode;
newnode->prev = phead;
}
void LTPopBack(LTNode* phead)
{
assert(phead);
LTNode* tail = phead->prev;
LTNode* tailprev = tail->prev;
free(tail);
phead->prev = tailprev;
tailprev->next = phead;
}
void LTPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
LTNode* tail = phead->next;
LTNode* next = tail->next;
phead->next = next;
next->prev = phead;
free(tail);
tail = NULL;
}
LTNode* LTFind(LTNode* phead, LTDateType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->val == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void LTInsert(LTNode* pos, LTDateType x)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* newnode = CreateLTNode(x);
posprev->next = newnode;
newnode->prev = posprev;
newnode->next = pos;
pos->prev = newnode;
}
void LTErase(LTNode* pos)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* posnext = pos->next;
posprev->next = posnext;
posnext->prev = posprev;
}
void LTDestroy(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
free(cur);
cur = next;
}
free(phead);
phead = NULL;
}
Through the implementation of the linked list above, we have already felt the convenience and simplicity of the headed bidirectional circular linked list. It does not need to consider whether the linked list has elements, and can also find the previous element, which provides great convenience in our use.
This content ends here. I hope you will gain something from reading this, and I would also like to thank all readers for their continued support. If you have any questions about the article, you can leave a message in the comment area. The blogger must revise it seriously and write better articles in the future.