Posted by Strikebf on Fri, 29 Oct 2021 15:06:28 +0200

The two-way leading circular linked list is the most complex one in structure, but it also brings advantages that other linked lists do not have because of its complex structure. Today we will introduce the two-way leading circular linked list and complete its construction.

# 1. What is a two-way cycle linked list?

Before explaining the two-way cycle linked list, let's take a look at the most simple linked list for comparison. Its structural characteristics are: one-way, no lead and no cycle. The structure diagram is as follows:

This linked list is characterized by simple structure and easy to understand. Each node has only one pointer to the next node, and the address of the previous node is not saved. Secondly, it has no header node, and the first node saves the data. Finally, the linked list does not have a circular structure. The post pointer of the last node stores a null pointer to indicate the end of the linked list.

Bidirectional linked list means that each node has a pointer to the next node and a pointer to the previous node. Its structure diagram is as follows:

Compared with the basic linked list, the bidirectional linked list does not need to traverse when deleting / adding nodes at random positions. Instead, it can find the front and rear nodes of the nodes to be deleted / added through the front pointer, and complete the deletion / addition of nodes at any position with O(1) time complexity  .

The leading node linked list means that the first node of the linked list does not store data, but is only used as the "head" of the linked list. This node is also called sentry position. Its structure diagram is as follows:

A linked list with a head node is much more convenient when operating the first node. If there is no head node, the pointer to the head of the linked list should be changed when the linked list is empty. With a head node, you only need to change the pointer of the head node.

The post pointer of the last node of the circular linked list does not save the null pointer, but the address of the first node. Its structure diagram is as follows:

For the single cycle linked list, its advantage is that it can traverse all nodes in the linked list from any node, and the more advantages of the cycle should be reflected in the two-way leading cycle linked list.

## 1.5 two way cycle linked list

Combined with the characteristics of the above three linked lists, you can get a two-way leading circular linked list. Its structure diagram is as follows:

Although the structure of this linked list looks very complex, it also contains the advantages of the above three linked lists, which can be well reflected when we realize it.

```typedef int ListDataType;
typedef struct ListNode
{
struct ListNode* next;//Post pointer
ListDataType data;//data
}list;```

Because it is a two-way linked list, in addition to the post pointer next, we also add the pre pointer prev to refer to the previous node. typedef is used to define the type of data, so as to change the data type saved in the data field.

```list* list_init()
{
{
printf("malloc fail");
exit(-1);
}
}```

The initialization function uses malloc to open up a dynamic space to save the header node. And initialize the head node so that its pre and post pointers point to itself, which means that the linked list is empty. Finally, the header node pointer is returned. We can receive the initialized header node by using a pointer outside the function. The following is an illustration of the initialized linked list:

```void list_destroy(list* head)
{
{
}
}```

When the destructor has more than one header node in the linked list, it first enters the loop for tail deletion. Because the linked list is a circular structure, when the post pointer of the head node points to itself, it means that only the head node is left in the linked list. At this point, exit the loop and release the U-turn node. However, it should be noted that the pointer outside the function becomes a wild pointer, and the destruction of the linked list is completed only by setting it null.

## Tail insertion

```void list_push_back(list* head,ListDataType x)
{
list* newnode = (list*)malloc(sizeof(list));//Create a new node
if (newnode == NULL)
{
printf("malloc fail");
exit(-1);
}

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

newnode->data = x;//Assign a value to the data field of the new node
}```

Because the linked list is a two-way cycle, we can easily find the tail node through the front pointer of the head pointer. The tail insertion can be completed by connecting the new tail node with the old tail node and head node.

## Tail deletion

```void list_pop_back(list* head)
{
list* backprev = head->prev->prev;//Save the penultimate node

}```

The assertion in the function related to deletion not only ensures the validity of the head node, but also ensures the existence of at least one node in the linked list. Otherwise, the head node may be released and serious errors may occur. After assertion, find and save the penultimate node through the front pointer, and then connect the penultimate node with the head node to complete tail deletion.

```void list_push_front(list* head,ListDataType x)
{
list* newnode = (list*)malloc(sizeof(list));
if (newnode == NULL)
{
printf("malloc fail");
exit(-1);
}

next->prev = newnode;
newnode->next = next;

newnode->data = x;//assignment
}```

First save the next node of the header node, then create a new node, and let the new node connect the upper header node and the next node to complete the header insertion.

```void list_pop_front(list* head)
{
list* next = head->next;//Save the node to release

free(next);//release
}```

Header deletion also ensures that there is at least one node in the linked list that saves data through assertion, otherwise the header node may be deleted. Then save the node to be deleted first, and the connection node can complete the header deletion.

```void list_print(list* head)
{
list* cur = head->next;//Start at the next position of the ab initio node
while (cur != head)//Ends the loop when the pointer points to the header node
{
printf("%d ", cur->data);//Here, take int data as an example
cur = cur->next;//The pointer points to the next node
}
printf("\n");
}```

Because there are header nodes, the nodes that traverse the linked list during output should traverse from the next node of the node. Because it is a loop structure, when cur points to the head node, it means that the linked list has completed a traversal, and then exit the loop.

## lookup

```list* list_find(list* head, ListDataType x)
{
while (cur != head)//When the traversal has not been found, exit the loop
{
if (cur->data == x)
return cur;
cur = cur->next;//cur points to the next node
}
}```

The search also needs to ensure that the linked list is traversed from the next node of the head node through assertion. If it is found, the pointer of the node is returned. Otherwise, when the pointer is equal to the head, it means that the linked list has been traversed but not found. Exit the loop and return NULL.

## Inserts a new node before the specified node

```void list_insert(list * pos, ListDataType x)
{
assert(pos);
list* prev = pos->prev;//Save the previous node for connection

list* newnode = (list*)malloc(sizeof(list));//Create a new node
if (newnode == NULL)
{
printf("malloc fail");
exit(-1);
}

prev->next = newnode;//Connection node
newnode->prev = prev;
pos->prev = newnode;
newnode->next = pos;

newnode->data = x;//assignment
}```

First save the previous node at the specified location to facilitate the connection of new nodes, and then create new nodes and connect them.

## Delete specified node

```void list_erase(list* pos)
{
assert(pos&&pos->next!=pos);//Assert

pos->prev->next = pos->next;//Connect front and rear nodes
pos->next->prev = pos->prev;

free(pos);//release
}```

Connect the nodes before and after the node to be deleted, and then release the space of the node to be deleted to complete the deletion.

# 3. Summary

By constructing a two-way leading circular linked list, we can find that although the structure of this linked list is complex:

2. Each node contains a pre pointer and a post pointer.

3. The post node of the last node points to the head node, and the front pointer of the head node points to the last node, forming a two-way loop.

It can be seen from the above implementation stage that although the structure of this linked list is very complex, it greatly reduces the difficulty of implementation: because of the circular structure, there is no need to find the tail during tail insertion. You can directly find the tail node through the front pointer of the head pointer for insertion. With the bidirectional structure, when inserting a node in front of any node, you do not need to traverse the linked list to find the previous pointer, but you can directly find the previous node through the previous pointer to complete the insertion. With the header node, there is no need to judge whether the linked list is empty during insertion.

And this structure also has great advantages in use: it saves a lot of traversal (such as finding the tail and finding the previous node), which greatly reduces the time complexity of this linked list in operation, and greatly improves the efficiency compared with the simplest linked list.

However, this structure also has a small disadvantage. Because of the existence of the front pointer, every time a node is created, it takes an extra pointer space. However, this can not deny that the two-way leading circular linked list is very practical.