[Data structure] Hand-torn two-way linked list

Table of contents

Preface

1. Doubly linked list 

The structure of a headed two-way circular linked list

2. Implementation of linked list

2.1 Initialization

2.2 Tail plug

2.3 Tail deletion

2.4 Head plug

2.5 Header deletion

2.6 Insert before pos position

2.7 Delete pos position

3. Complete source code of doubly linked list

List.h

List.c


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. 

Guess you like

Origin blog.csdn.net/m0_68662723/article/details/134416069