Data structure doubly linked list

Insert image description here

Hello, long time no see. Today we are going to talk about the doubly linked list of the linked list. This is a very powerful linked list. It is bidirectional and cyclic. After learning this linked list, you will find that deleting the head plug of the sequence list is no longer a troublesome problem. For single linked lists, End insertion and end deletion have become simpler, so without further ado, let’s start our study!

First of all, we need to understand its physical and logical structure. So what do they look like? First, there is a leader. Is this our sentinel position? It is bidirectional and cyclic. Let us draw a picture to understand it. Bar.

Insert image description here

It roughly has a shape like this. Then we now need such a structure to implement such a function. First, there should be one to store data, which is data, and then there must be two pointers, one pointing to the previous node and the other pointing to For the following nodes, let's call them one pre pointing to the front and one next pointing to the back. Let's implement it.

typedef int DListType;
typedef struct DList
{
    
    
	struct DList* pre;
	struct DList* next;
	DListType data;
	
}DLNode;

In order to make it easier for us to write the structure later, we first define the structure as DLNode, which is more convenient to use.

Now we need to implement the following various interfaces to complete the double linked list

The first and most important
thing is how to initialize. When initializing, we must first think about the parameters and return values ​​of this interface function.
Because it is a doubly linked list, there must be a head node so that the following content can be linked.

Initialize doubly linked list

DLNode* DListInit();

The name of the interface function.
Here we analyze why the return value is not void but DLNode*
because we need to create a head node here, which we will use later. Secondly, there is another reason why the head node is not will be destroyed. Of course, we can also create a node outside, and then we can pass its pointer in and modify the content of the structure to achieve the same effect. Without further ado, let’s implement it!

DLNode* DListInit()
{
    
    
	DLNode* pHead = (DLNode*)malloc(sizeof(DLNode));
	assert(pHead);
	pHead->next = pHead;
	pHead->pre = pHead;
}

In fact, it is very simple. The pointer must point to itself. If not, our loop cannot be implemented.
The next step is how to print. Printing is very simple, we just pass it the pointer.

Print doubly linked list

void DListPrint(DLNode* pHead)
{
    
    
	assert(pHead);
	DLNode* cur = pHead->next;
	while (cur != pHead)
	{
    
    
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

The idea of ​​the print function is that we find the node after head, and then print it. Because we do not print the head node, we start with cur. Because it is a loop, cur will eventually equal pHead. In this case, it is a loop, so we find the next one. That's it, then update cur after printing.
Next we need to create a node so that we can link our nodes.

DLNode* CreatNewNode(DListType x)
DLNode* CreatNewNode(DListType x)
{
    
    
	DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;
	newnode->pre = NULL;
	return newnode;
}

After creating the node, the next step is to insert a node into the tail. When we insert the tail of the singly linked list, we must first find the position of the tail, and then insert the tail. At the same time, we must first find the previous position of the tail, and then free the tail. This is complicated in time. The degree is O(N), so the efficiency is not particularly high. Now, because the position of the head stores the position of the tail, there is no need to search. Let’s implement it now

Double linked list end plug

void DListPushBACK(DLNode* pHead, DListType x)
{
    
    
	assert(pHead);
	DLNode* newnode = CreatNewNode(x);
	DLNode* pre = pHead->pre;
	pHead->pre = newnode;
	newnode->next = pHead;
	pre->next = newnode;
	newnode->pre = pre;
}

In fact, tail insertion is also very simple. We use a pointer to save the position here. In this way, we can find the tail position next time we insert it, and we don’t have to pay attention to the order, which greatly improves efficiency.

If there is a tail plug, then there must be a head plug. Let’s implement it in the same way.

Header of doubly linked list


void DListPushFront(DLNode* pHead, DListType x)
{
    
    
	assert(pHead);
	DLNode* newnode = CreatNewNode(x);
	DLNode* next = pHead->next;
	pHead->next = newnode;
	newnode->pre = pHead;
	newnode->next = next;
	next->pre = newnode;
}

With head insertion and these, there must be head deletion and tail deletion.
Let's implement it too.

tail delete

void DListPopBack(DLNode* pHead)
{
    
    
	assert(pHead);
	if (pHead->next != pHead)
	{
    
    
		DLNode* del = pHead->pre;
		del->pre->next = pHead;
		pHead->pre = del->pre;
		free(del);
	}
}

All the codes written earlier need to be tested. Let’s test three of them first.

#include"DList.h"
void test()
{
    
    
	DLNode* head = DListInit();
	DListPushBack(head, 1);
	DListPushBack(head, 2);
	DListPushBack(head, 3);
	DListPushBack(head, 4);
	DListPrint(head);
	DListPushFront(head, 111);
	DListPushFront(head, 111);
	DListPushFront(head, 111);
	DListPrint(head);
	DListPopBack(head);
	DListPopBack(head);
	DListPopBack(head);
	DListPrint(head);
}
int main()
{
    
    
	test();
	return 0;
}

We found that there is no problem with our program. Let’s implement head deletion again.

void DListPopFront(DLNode* pHead)
{
    
    
	assert(pHead);
	if (pHead->next != pHead)
	{
    
    
		DLNode* del = pHead->next;
		pHead->next = del->next;
		del->next->pre = pHead;
		free(del);
	}
}

The next step is to search the doubly linked list. With search, we can use it with deletion and insertion at any position. Let's implement it now.


DLNode* DListFind(DLNode* pHead, DListType x)
{
    
    
	assert(pHead);
	DLNode* cur = pHead->next;
	while (cur != pHead)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

random insertion

void DListInsert(DLNode* pos, DListType x)
{
    
    
	assrt(pos);
	DLNode* newnode = CreatNewNode(x);
	DLNode* pospre = pos->pre;
	pospre->next = newnode;
	newnode->pre = pospre;
	newnode->next = pos;
	pos->pre = newnode;

}

It is not a random insertion, it is inserted before the pos position. We just pass pos and x directly, and then insert the link step by step according to the previous idea.

Here you can update the head insertion and tail insertion, which will not be demonstrated here.
Then we are writing a function to delete the pos position.

Delete pos position

void DListPop(DLNode* pos)
{
    
    
	assert(pos);
	DLNode* pospre = pos->pre;
	DLNode* posnext = pos->next;
	pospre->next = posnext;
	posnext->pre = pospre;
	free(pos);
}

There is also a destroy space that we opened up. This is to traverse the array and release them one by one. However, there will be problems. When we release, we must remember the location carefully. We can release it from the tail and use a pointer to remember the previous one. position, then release the tail, and then update the previous position, just use while to control it.

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DListType;
typedef struct DList
{
    
    
	struct DList* pre;
	struct DList* next;
	DListType data;
	
}DLNode;

DLNode* DListInit();

void DListPrint(DLNode* pHead);

DLNode* CreatNewNode(DListType x);

void DListPushBack(DLNode* pHead, DListType x);

void DListPushFront(DLNode* pHead, DListType x);


void DListPopBack(DLNode* pHead);

void DListPopFront(DLNode* pHead);

DLNode* DListFind(DLNode* pHead, DListType x);

void DListInsert(DLNode* pos, DListType x);

void DListPop(DLNode* pos);
#include"DList.h"

DLNode* DListInit()
{
    
    
	DLNode* pHead = (DLNode*)malloc(sizeof(DLNode));
	pHead->next = pHead;
	pHead->pre = pHead;
}

void DListPrint(DLNode* pHead)
{
    
    
	assert(pHead);
	DLNode* cur = pHead->next;
	while (cur != pHead)
	{
    
    
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

DLNode* CreatNewNode(DListType x)
{
    
    
	DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
	assert(newnode);
	newnode->data = x;
	newnode->next = NULL;
	newnode->pre = NULL;
	return newnode;
}


void DListPushBack(DLNode* pHead, DListType x)
{
    
    
	DLNode* newnode = CreatNewNode(x);
	DLNode* pre = pHead->pre;
	pHead->pre = newnode;
	newnode->next = pHead;
	pre->next = newnode;
	newnode->pre = pre;
}

void DListPushFront(DLNode* pHead, DListType x)
{
    
    
	DLNode* newnode = CreatNewNode(x);
	DLNode* next = pHead->next;
	pHead->next = newnode;
	newnode->pre = pHead;
	newnode->next = next;
	next->pre = newnode;
}

void DListPopBack(DLNode* pHead)
{
    
    
	assert(pHead);
	if (pHead->next != pHead)
	{
    
    
		DLNode* del = pHead->pre;
		del->pre->next = pHead;
		pHead->pre = del->pre;
		free(del);
	}
}

void DListPopFront(DLNode* pHead)
{
    
    
	assert(pHead);
	if (pHead->next != pHead)
	{
    
    
		DLNode* del = pHead->next;
		pHead->next = del->next;
		del->next->pre = pHead;
		free(del);
	}
}

DLNode* DListFind(DLNode* pHead, DListType x)
{
    
    
	assert(pHead);
	DLNode* cur = pHead->next;
	while (cur != pHead)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

void DListInsert(DLNode* pos, DListType x)
{
    
    
	assrt(pos);
	DLNode* newnode = CreatNewNode(x);
	DLNode* pospre = pos->pre;
	pospre->next = newnode;
	newnode->pre = pospre;
	newnode->next = pos;
	pos->pre = newnode;

}


void DListPop(DLNode* pos)
{
    
    
	assert(pos);
	DLNode* pospre = pos->pre;
	DLNode* posnext = pos->next;
	pospre->next = posnext;
	posnext->pre = pospre;
	free(pos);
}

Thank you everyone, see you next time

Guess you like

Origin blog.csdn.net/2301_76895050/article/details/132501633