[Data Structure] Detailed explanation of doubly linked list

Insert image description here
After we finish learning the singly linked list, the doubly linked list is much simpler. In the doubly linked list, head insertion, tail insertion, head deletion, tail deletion, as well as insertion at any position and deletion at any position are simpler than in a singly linked list. Today, follow Xiao Zhang Let’s learn together! !

Classification of doubly linked lists

Two-way linked list without header

Insert image description here

Two-way headed circular linked list

Insert image description here

There are also two-way headed non-cyclic linked lists, two-way non-headed circular linked lists, focusing on the use of two-way headed circular linked lists, the leader also has a sentinel position.

Two-way headed circular linked list

Headed two-way circular linked list: the most complex structure, generally used to store data separately. The linked list data structures used in practice are all headed bidirectional circular linked lists. In addition, although this 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 simple. We will know it later when we implement the code.

Interface implementation of two-way circular linked list

ListNode* ListCreate(int x)//创建新结点
ListNode* createhead()//创建哨兵位
void ListPushBack(ListNode* phead, int x)//尾插
void SListPrint(ListNode* phead)//打印链表
void SListPushFront(ListNode* phead, int x)//头插
void SListPopBack(ListNode* phead)//尾删
void SListPopFront(ListNode* phead)//头删
void SListInsert(ListNode* pos, int x)//pos前插
void SListErase(ListNode* pos)//删除pos;
ListNode* SListFind(ListNode* phead,int x)//查找链表中第一个x

0.Creation of node structure

typedef struct ListNode
{
    
    
	int data;//数据
	struct ListNode* next;//下个结点地址
	struct ListNode* prev;//上个结点地址
}ListNode;

1. Create a new node

ListNode* ListCreate(int x)//创建新结点
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));//给新结点申请空间
	if (newnode == NULL)
	{
    
    
		perror("malloc error");//没申请到,malloc返回NULL给newnode
		
	}
	newnode->data = x;//新结点的数据给x
	newnode->next = NULL;//新结点的下一个结点地址为空
	newnode->prev = NULL;//新结点的上一个结点地址为空
	return newnode;//新结点的地址返回回去
}

2. Create sentinel positions

This sentinel bit serves as the head node of the linked list and does not store data.

ListNode* createhead()
{
    
    
	ListNode* head = ListCreate(-1);//哨兵位结点的数据随便给个-1
	head->next = head;
	head->prev = head;
	return head;//返回哨兵位的头结点地址
}

Insert image description hereWhen there is no new node with data, first let the address of the previous node and the address of the next node be stored in the head's own address.

3.Tail plug

void ListPushBack(ListNode* phead, int x)//尾插
{
    
    
	ListNode* newnode = ListCreate(int x);
	ListNode* tail = phead->prev;//记录尾结点的地址
	newnode->prev =tail;//新结点的prev存放尾结点的地址-1
	newnode->next = phead;//新结点的next存放头结点哨兵位的地址->2
	phead->prev = newnode;//头结点的prev存放新结点的地址->4
	tail->next = newnode;//尾结点的next存放新节点的地址->3

}

Analysis: Insert image description here
Since tail is used to record the address of the tail node, the order of 1, 2, 3, and 4 can be switched arbitrarily. If there is no record, remember to save the prev and next of the new node first, and then modify the head-> prev, head->prev->next; prevent the address of the tail node from being lost during the modification process.
Insert image description here
The same applies to inserting new nodes after the sentinel position

4. Print double linked list

void SListPrint(ListNode* phead)//打印链表
{
    
    
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		printf("%d->", cur->data);
		cur = cur->next;
    }
	printf("\n");


}

Analysis: Define a pointer cur to traverse the entire linked list. Since it is a circular linked list, it will definitely point to the sentinel bit. The data of the sentinel bit node is not used, so the cur pointer starts traversing from the node next to the sentinel bit of the head node until cur==phead , the loop ends, print cur->data each time, and then move cur to the next node.

5.Head plug

void SListPushFront(ListNode* phead, int x)//头插
{
    
    
	ListNode* newnode = ListCreate(x);
	
	newnode->next = phead->next;
	phead->next->prev = newnode;
	newnode->prev = phead;
	phead->next = newnode;

}

analyze:Insert image description here
Notice: First save newnode->next; and newnode->prev;
if you proceed to step 4 first, the address of phead->next will become newnode, and the previous address of phead->next will be lost.
Another method is to save * next=phead->next; and then 1, 2, 3, 4, the order can be reversed.

6.Tail deletion

void SListPopBack(ListNode* phead)//尾删
{
    
    
	ListNode* last = phead->prev->prev;//记录尾结点的上一个结点的地址
	phead->prev = last;//头节点哨兵位的prev存入last的地址
	last->next = phead;//last的next存入phead的地址

}

Analysis: Insert image description hereWhen we want to delete the tail node, we can first record the address of the previous node of the tail node, because to delete the tail node, the change is to store the address of phead next to the last node of the tail node. The prev of phead stores the address of last. In
Insert image description hereaddition to the sentinel bit, there is also a tail deletion of the node, leaving a sentinel bit head node. The same code above applies.

7. Header deletion

void SListPopFront(ListNode* phead)//头删
{
    
    
	ListNode* next = phead->next;
	phead->next = next->next;
	next->next->prev = phead;
}

Analysis: Insert image description hereFirst save the next node address of the sentinel bit to next, then store the next of phead into next->next; store next->next->prev into the first address of phead

8. Insert the node pointed to by the pos pointer forward

void SListInsert(ListNode* pos, int x)//pos前插
{
    
    
	ListNode* newnode = ListCreate(x);//申请新结点将地址存放在newnode变量中
	newnode->next = pos;//新结点的下一个结点保存pos指针指向节点的地址
	newnode->prev = pos->prev;//新结点的prev保存pos指针指向的节点的上一个节点的地址
	pos->prev->next=newnode;//pos指针指向的节点的前一个结点的next保存newnode指针指向的结点
	pos->prev = newnode;//pos指针指向的结点的prev保存newnode指针指向结点的地址
}

analyze:Insert image description here

9. Delete the node pointed to by the pos pointer

void SListErase(ListNode* pos)//删除pos;
{
    
    
	ListNode* last = pos->prev;//记录pos指针指向结点的前一个结点地址
	ListNode* next = pos->next;//记录pos指针指向结点的后一个结点地址
	last->next = next;//操作1
	next->prev = last;//操作2

}

analyze:Insert image description here

10. Find the first occurrence of x in the linked list

ListNode* SListFind(ListNode* phead,int x)
{
    
    
	ListNode* cur = phead->next;//cur指针先指向phead的下一个结点的位置
	while (cur != phead)//循环遍历
	{
    
    
		if (cur->data == x)//第一次找到
		{
    
    
			return cur;返回结点数据等于x的地址



		}
		cur = cur->next;//cur指针指向下一个结点





	}
	return NULL;
}

Analysis: Similar to printing a printed linked list, the loop is traversed, starting from phead->next. When cur is not equal to phead, the loop continues. If cur->datax, returns the content of cur, which is dataIf the node address of x is not found at the end of the loop, it means that the linked list does not have a node equal to x and NULL is returned;

11. Destruction of doubly linked list

void Destroy(ListNode* phead)
{
    
    
	
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

Analysis, define a pointer cur to first point to the next node of phead, start the traversal, first save the address of the next node of the node pointed by cur to next, then release the space pointed by the cur pointer, and then let cur point to the node that has been saved in The address of the next node in next is cycled to release all nodes in sequence. At the end of the cycle, the sentinel bit is released.

12. Complete source code

#include <stdio.h>
#include <stdlib.h>
typedef struct ListNode
{
    
    
	int data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;
ListNode* ListCreate(int x)//创建新结点
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
    
    
		perror("malloc error");
		//return;
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}
ListNode* createhead()
{
    
    
	ListNode* head = ListCreate(-1);
	head->next = head;
	head->prev = head;
	return head;
}
void ListPushBack(ListNode* phead, int x)//尾插
{
    
    
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->data = x;
	ListNode* tail = phead->prev;
	newnode->prev =tail;
	phead->prev = newnode;
	tail->next = newnode;
	newnode->next = phead;
	

}
void SListPrint(ListNode* phead)//打印链表
{
    
    
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		printf("%d->",cur->data);
		cur = cur->next;
    }
	printf("\n");


}
void SListPushFront(ListNode* phead, int x)//头插
{
    
    
	ListNode* newnode = ListCreate(x);
	
	newnode->next = phead->next;
	phead->next->prev = newnode;
	newnode->prev = phead;
	phead->next = newnode;

}
void SListPopBack(ListNode* phead)//尾删
{
    
    
	ListNode* last = phead->prev->prev;
	phead->prev = last;
	last->next = phead;

}
void SListPopFront(ListNode* phead)//头删
{
    
    
	ListNode* next = phead->next;
	phead->next = next->next;
	next->next->prev = phead;
}
void SListInsert(ListNode* pos, int x)//pos前插
{
    
    
	ListNode* newnode = ListCreate(x);
	

	newnode->next = pos;
	newnode->prev = pos->prev;
	pos->prev->next=newnode;
	pos->prev = newnode;
}
void SListErase(ListNode* pos)//删除pos;
{
    
    
	ListNode* last = pos->prev;
	ListNode* next = pos->next;
	last->next = next;
	next->prev = last;

}
ListNode* SListFind(ListNode* phead,int x)
{
    
    
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;



		}
		cur = cur->next;





	}
	return NULL;
}
//双链表的销毁
void Destroy(ListNode* phead)
{
    
    
	
	ListNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}
int main()
{
    
    
	ListNode* list;
	list = createhead();
	printf("尾插:");
    ListPushBack(list,1);
	ListPushBack(list,2);
	ListPushBack(list,3);
	ListPushBack(list,4);
	SListPrint(list);
	printf("头插:");
	SListPushFront(list, 8);
	SListPushFront(list, 7);
	SListPushFront(list, 6);
	SListPushFront(list, 5);
	SListPrint(list);
	printf("尾删:");
	SListPopBack(list);
	SListPopBack(list);
	SListPrint(list);
	printf("头删:");
	SListPopFront(list);
	SListPopFront(list);
	SListPopFront(list);
	SListPrint(list);
	printf("插入pos指向结点前面:");
	SListInsert(list->next->next, 1000);
	SListPrint(list);
	printf("删除pos指向的结点:");
	SListErase(list->next->next);
	SListPrint(list);
	printf("查找第一个x的位置并打印出来:");
	ListNode* p=SListFind(list, 8);
	printf("%d", p->data);
	printf("修改查找到的结点:\n");
	p->data = 10000;
	SListPrint(list);
	printf("销毁打印销毁后哨兵位的下一个结点的数据,如果为随机值说明已经被销毁:");
	Destroy(list);
	printf("%d",list->data);
	
	
}

13. Compile and run

Insert image description here

Guess you like

Origin blog.csdn.net/yyqzjw/article/details/132812838