Implementation of bidirectional linked list

        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

Structure creation

Bidirectional linked list function interface definition

Implementation of function interface

LTNode* ListInit()

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

List.h

List.c

Code test.c

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)

Tags: data structure linked list

Posted on Sat, 30 Oct 2021 11:48:20 -0400 by grantc2