Introduction to data structure - detailed explanation of linked list (SeqList) (initialization, addition, deletion, query, modification)

1. Introduction to linked list

1.1 The concept and structure of linked list


Concept: Linked list is a non-sequential and non-sequential storage structure in physical storage structure. The logical order of data elements is realized through the linking order of pointers in the linked list .
insert image description here
Pay attention to three points:

  1. It can be seen from the figure that the structure of the linked list is logically continuous, but not necessarily continuous in memory.
  2. The nodes of the linked list are created on the heap.
  3. The two spaces created on the heap may or may not be continuous.

1.2 Classification of linked lists

One-way and two-way
one- way linked list
insert image description here

Doubly linked list
insert image description here
with head and without head,
with head
insert image description here
, without head
insert image description here
loop and without loop

Circular
insert image description here
non-circular
insert image description here
Combining these three types of permutations can form 8 kinds of linked lists
We mainly explain two types
1. One-way non-leading non-circular linked list
2. Two-way leading circular linked list

Two. One-way non-leading non-circular linked list

insert image description here
Headless one-way non-circular linked list : simple structure, generally not used to store data alone. In practice, it is more of
a substructure of other data structures, such as hash buckets, adjacency lists of graphs, and so on. In addition, this structure appears a lot in written test interviews.

2.1 Implementation code of singly linked list

The general structure can be shown by the following code:
insert image description here
datait represents the data stored in each node, and nextit represents the pointer to the next node. If there is no next node, then next will be empty.

2.1.1 Header file - SList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLTDateType;
typedef struct SListNode
{
    
    
	SLTDateType data;
	struct SListNode* next;
}SListNode;

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
//创建一个有n个节点的单链表 
SListNode* CreatSListNode(int n);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x);
// 单链表在pos位置之前插入x
void SListInsertBefore(SListNode** plist, SListNode* pos, SLTDateType x);
// 单链表删除pos之后位置
void SListEraseAfter(SListNode* pos);
//单链表删除pos位置
void SListErase(SListNode** pplist, SListNode* pos);
// 单链表的销毁
void SListDestroy(SListNode** plist);

2.1.2 Specific function implementation file——SList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SList.h"
// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x)
{
    
    
	SListNode* phead = (SListNode*)malloc(sizeof(SListNode));
	if (phead == NULL)
	{
    
    
		perror("malloc failed");
		exit(-1);
	}
	else
	{
    
    
		phead->data = x;
		phead->next = NULL;
	}
	return phead;
}
//创建一个有n个节点的单链表 
SListNode* CreatSListNode(int n)
{
    
    
	SListNode* phead = NULL, * ptail = NULL;
	for (int i = 0; i < n; i++)
	{
    
    
		SListNode* NewNode = BuySListNode(i);
		if (phead == NULL)
		{
    
    
			ptail = phead = NewNode;
		}
		else
		{
    
    
			ptail->next = NewNode;
			ptail = NewNode;
		}
	}
	return phead;
}

// 单链表打印
void SListPrint(SListNode* plist)
{
    
    
	SListNode* cur = plist;
	while (cur)
	{
    
    
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
    
    
	SListNode* NewNode = BuySListNode(x);
	//链表为空时也可以尾插
	if (*pplist == NULL)
	{
    
    
		*pplist = NewNode;
	}
	else
	{
    
    
		SListNode* tail = *pplist;
		while (tail->next)
		{
    
    
			tail = tail->next;
		}
		tail->next = NewNode;
	}
}
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
    
    
	SListNode* NewNode = BuySListNode(x);
	NewNode->next = *pplist;
	*pplist = NewNode;

}
// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
    
    
	//链表为空就删不了了
	assert(*pplist);
	SListNode* tail = *pplist;
	//一上来就已经是尾
	if (tail->next == NULL)
	{
    
    
		free(tail);
		*pplist = NULL;
	}
	else
	{
    
    
		方法1
		//while (tail->next->next)
		//{
    
    
		//	tail = tail->next;
		//}
		//free(tail->next);
		//tail->next = NULL;

		//方法2
		SListNode* pre = NULL;
		while (tail->next)
		{
    
    
			pre = tail;
			tail = tail->next;
		}
		free(tail);
		pre->next = NULL;
	}
}

// 单链表头删
void SListPopFront(SListNode** pplist)
{
    
    
	assert(*pplist);
	SListNode* tmp = (*pplist)->next;
	free(*pplist);
	*pplist = tmp;
}

// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
    
    
	SListNode* cur = plist;
	while (cur)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;
		}
		else
		{
    
    
			cur = cur->next;
		}
	}
}
// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{
    
    
	if (pos == NULL)
	{
    
    
		return;
	}
	else
	{
    
    
		SListNode* newnode = BuySListNode(x);
		newnode->next = pos->next;
		pos->next = newnode;
	}
}
// 单链表在pos位置之前插入x
void SListInsertBefore(SListNode** pplist, SListNode* pos, SLTDateType x)
{
    
    
	if (*pplist == pos)
	{
    
    
		SListPushFront(pplist, x);
	}
	else
	{
    
    
		SListNode* newnode = BuySListNode(x);
		SListNode* pre = *pplist;
		while (pre->next != pos)
		{
    
    
			pre = pre->next;
		}
		newnode->next = pre->next;
		pre->next = newnode;

	}
}
// 单链表删除pos之后位置
void SListEraseAfter(SListNode* pos)
{
    
    
	assert(pos);
	if (pos->next == NULL)
	{
    
    
		return;
	}
	else
	{
    
    
		SListNode* tmp = pos->next;
		pos->next = pos->next->next;
		free(tmp);
	}
}
// 单链表删除pos当前位置
void SListErase(SListNode** pplist, SListNode* pos)
{
    
    
	assert(pos && *pplist);
	if (*pplist == pos)
	{
    
    
		SListPopFront(pplist);
	}
	else
	{
    
    
		SListNode* cur = *pplist;
		while (cur->next != pos)
		{
    
    
			cur = cur->next;
		}
		cur->next = pos->next;
		free(pos);
	}
}
// 单链表的销毁
void SListDestroy(SListNode** pplist)
{
    
    
	SListNode* pre = *pplist;
	SListNode* cur = *pplist;
	while (cur->next)
	{
    
    
		pre = cur->next;
		free(cur);
		cur = pre;
	}
	*pplist = NULL;
}

Regarding the test of the program, you can write a main function for testing.

3. Two-way headed circular linked list

insert image description here
Leading two-way circular linked list : the most complex structure, generally used to store data separately. The linked list data structure used in practice
is the leading bidirectional circular linked list. 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 simpler. We will know when we implement the code later.

3.1 Implementation code of bidirectional leading circular linked list

The code of the structure declaration:
insert image description here
the structure of the two-way leading circular linked list is relatively complicated, but the realization of each function is actually very simple. Please see the code next

3.1.1 Header file - DList.h

#pragma once
//头文件包含
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

//结构声明
typedef int DLTDataType;//声明存储的数据类型
typedef struct DoubleListNode
{
    
    
	DLTDataType data;
	struct DoubleListNode* prev;
	struct DoubleListNode* next;
}DLTNode;//声明链表的节点

//函数声明

//创建新节点
DLTNode* BuyNode(DLTDataType x);
//初始化链表
DLTNode* DLTInit();
//打印链表
void DLTPrint(DLTNode* phead);
//链表头插
void DLTPushFront(DLTNode* phead, DLTDataType x);
//链表头删
void DLTPopFront(DLTNode* phead);
//链表尾插
void DLTPushBack(DLTNode* phead, DLTDataType x);
//链表尾删
void DLTPopBack(DLTNode* phead);
//链表查找
DLTNode* DLTFind(DLTNode* phead, DLTDataType x);
//在链表的pos节点前插入数据
void DLTInsertBefore(DLTNode* phead, DLTNode* pos, DLTDataType x);
//删除链表中pos节点的数据
void DLTErase(DLTNode* phead, DLTNode* pos);

3.1.2 Concrete function realization——DList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "DList.h"
//创建新节点
DLTNode* BuyNode(DLTDataType x)
{
    
    
	DLTNode* newNode = (DLTNode*)malloc(sizeof(DLTNode));
	if (newNode == NULL)
	{
    
    
		perror("malloc failed");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;
	return newNode;
}
//初始化链表
DLTNode* DLTInit()
{
    
    
	DLTNode* phead = BuyNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}
//打印链表
void DLTPrint(DLTNode* phead)
{
    
    
	assert(phead);
	DLTNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
//链表头插
void DLTPushFront(DLTNode* phead, DLTDataType x)
{
    
    
	assert(phead);
	//开辟新节点
	DLTNode* newNode = BuyNode(x);
	//保存原本的第一个节点
	DLTNode* tmp = phead->next;
	//更新链表
	phead->next = newNode;
	newNode->prev = phead;
	newNode->next = tmp;
	tmp->prev = newNode;
}
//链表头删
void DLTPopFront(DLTNode* phead)
{
    
    
	assert(phead);
	assert(phead->next != phead);
	//保存要删除的节点
	DLTNode* tmp = phead->next;
	//更新链表
	phead->next = tmp->next;
	tmp->next->prev = phead;
	//删除保存的节点
	free(tmp);
}
//链表尾插
void DLTPushBack(DLTNode* phead, DLTDataType x)
{
    
    
	assert(phead);
	//申请新节点
	DLTNode* newNode = BuyNode(x);
	//保存原本的尾节点
	DLTNode* tmp = phead->prev;
	//更新链表
	tmp->next = newNode;
	newNode->prev = tmp;
	phead->prev = newNode;
	newNode->next = phead;
}
//链表尾删
void DLTPopBack(DLTNode* phead)
{
    
    
	assert(phead);
	assert(phead->next != phead);
	//保存尾结点
	DLTNode* tmp = phead->prev;
	//将尾结点的上一个节点更新为新的尾结点
	tmp->prev->next = phead;
	phead->prev = tmp->prev;
	//释放原本的尾结点
	free(tmp);
}
//链表查找
DLTNode* DLTFind(DLTNode* phead, DLTDataType x)
{
    
    
	assert(phead);
	DLTNode* cur = phead->next;
	while (cur != phead)
	{
    
    
		if (cur->data == x)
		{
    
    
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

//在链表的pos节点前插入数据
void DLTInsertBefore(DLTNode* phead, DLTNode* pos, DLTDataType x)
{
    
    
	assert(phead);
	assert(pos);
	//申请一个新节点
	DLTNode* newNode = BuyNode(x);
	//更新链表
	pos->prev->next = newNode;
	newNode->prev = pos->prev;
	newNode->next = pos;
	pos->prev = newNode;

}
//删除链表中pos节点的数据
void DLTErase(DLTNode* phead, DLTNode* pos)
{
    
    
	assert(phead);
	assert(pos);
	assert(phead->next != phead);
	DLTNode* tmp = pos->next;
	pos->prev->next = tmp;
	tmp->prev = pos->prev;
	free(pos);
}

4. Comparison between sequence list and linked list

insert image description here

V. Summary

In fact, the main idea is still mentioned in the code, thank you for reading it patiently. A subsequent blog will talk about OJ questions about linked lists.
insert image description here

Guess you like

Origin blog.csdn.net/m0_72482689/article/details/127779966