单链表和双向链表

发布于:2025-07-03 ⋅ 阅读:(14) ⋅ 点赞:(0)

目录

目录

目录

一、链表种类

二、单链表概念

三、单链表实现

3.1 单链表创建结点

3.2 单链表销毁

3.3 单链表尾插

3.4 单链表尾删

3.5 单链表头插

3.6 单链表头删

3.7 单链表寻找值

3.8 单链表任意插(之前、之后)

3.9 单链表任意删(当前、后面)

四、双向链表概念

五、双向链表实现

4.1 双向链表创建结点

4.2 双向链表销毁

4.3 双向链表尾插

4.4 双向链表尾删

4.5 双向链表头插

4.6 双向链表头删

4.7 双向链表寻找值

4.8 双向链表任意插(之前、之后)

4.9 双向链表任意删

六、总结与反思(单双链表优缺点)


一、链表种类

        链表分为带头(不带头)、单向(双向)和循环(不循环)这几个分类。

二、单链表概念

        单链表是⼀种物理存储结构(不一定连续)上非连续、非顺序的存储结构,数据元素的逻辑顺序(连续)是通过链表中的指针链接次序实现的。他是不带头单向不循环链表,底层是结构体。

三、单链表实现

3.1 单链表创建结点

        创建结点时先写出单链表的结构体,如下:

//结构体
typedef int SLTDataType;
typedef struct SListNode
{
    SLTDataType data;
    struct SListNode* next;
}SLTNode;

创建结点

//创造节点
SLTNode* SLTbuyNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;
	return newnode;
}

3.2 单链表销毁

//销毁
void SListDestroy(SLTNode** pphead)//这里用二级指针是通过修改*phead这个指针地址来修改内部值(不带头)
{
	assert(*pphead);
	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

3.3 单链表尾插

//尾插
void SLTPushBack(SLTNode** pphead, SLTDataType x)
{
	assert(pphead);
	SLTNode* ptail = *pphead;
	//创建节点
	SLTNode* newnode = SLTbuyNode(x);
	//考虑没节点
	if (ptail == NULL)
	{
		*pphead = newnode;
	}
	//找尾节点
	else
	{
		while (ptail->next)
		{
			ptail = ptail->next;
		}
		ptail->next = newnode;
	}
	
}

3.4 单链表尾删

//尾删
void SLTPopBack(SLTNode** pphead)
{
	assert(*pphead);
	SLTNode* pcur = *pphead;
	SLTNode* prev = NULL;
	SLTNode* ptail = pcur;
	//考虑只有一个尾项删除
	if (ptail->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else 
	{
		while (ptail->next)
		{
			prev = ptail;
			ptail = ptail->next;
		}
		prev->next = NULL;
		free(ptail);
		ptail = NULL;
	}
	
}

3.5 单链表头插

//头插
void SLTPushFront(SLTNode** pphead,SLTDataType x)
{
	assert(pphead);
	//创造节点
	SLTNode* newnode = SLTbuyNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

3.6 单链表头删

//头删
void SLTPopFront(SLTNode** pphead)
{
	assert(*pphead);
	SLTNode* pcur = *pphead;
	SLTNode* next = (*pphead)->next;
	free(pcur);
	pcur = NULL;
	*pphead = next;
}

3.7 单链表寻找值

//查询
SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
{
	assert(phead);
	SLTNode* pcur = phead;
	while (pcur)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

3.8 单链表任意插(之前、之后)

//任意位置之前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(*pphead);
	SLTNode* pcur = *pphead;
	SLTNode* newnode = SLTbuyNode(x);
	//考虑重回时pcur和pos
	if (pcur == pos)
	{
		SLTPushFront(pphead, x);
	}
	//不重合时
	else
	{
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		newnode->next = pcur->next;
		pcur->next = newnode;
	}
}

//任意位置之后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);
	SLTNode* newnode = SLTbuyNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

3.9 单链表任意删(当前、后面)

//任意删
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(*pphead);
	SLTNode* pcur = *pphead;
	SLTNode* next = pos->next;
	//头删
	if (pcur == pos)
	{
		*pphead = pcur->next;
	}
	else
	{
		while (pcur->next != pos)
		{
			pcur = pcur->next;
		}
		pcur->next = next;
		free(pos);
		pos = NULL;
	}
}


//任意删pos后面
void SLTEraseAfter(SLTNode** pphead, SLTNode* pos)
{
	assert(*pphead && pos->next != NULL);
	SLTNode* pcur = *pphead;
	SLTNode* next = pos->next;
	while (pcur != pos)
	{
		pcur = pcur->next;
	}
	pcur->next = next->next;
	free(next);
	next = NULL;
}

四、双向链表概念

        双向链表是一种数据结构,每个节点包含三个部分:数据、指向前一个节点的指针(prev)和指向下一个节点的指针(next)。双向链表是带头的双向循环链表,底层是结构体。

五、双向链表实现

4.1 双向链表创建结点

        定义双向链表 

//双向链表结构定义
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType Data;//数据
	struct ListNode* prev;
	struct ListNode* next;
}ListNode;

         创建头结点

//创建头结点
ListNode* ListCreate()
{
	ListNode* pHead = (ListNode*)malloc(sizeof(ListNode));
	if (pHead == NULL)
	{
		perror("malloc1");
		exit(1);
	}
	pHead->Data = -1;
	pHead->next = pHead->prev = pHead;
	return pHead;
}

        创建结点

//创造新节点
ListNode* ListbuyNode(LTDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc2");
		exit(1);
	}
	newnode->Data = x;
	newnode->prev = newnode->next = newnode;
	return newnode;
	
}

4.2 双向链表销毁

//销毁
void ListDestory(ListNode* pHead)//这里不用二级指针是由于不需要改变pHead这个头结点,只要通过pHead这个指针去访问并可修改下个指针即可
{
	ListNode* del = pHead->next;
	while (del != pHead)
	{
		ListNode* next = del->next;
		free(del);
		del = next;
	}
	pHead = NULL;
}

4.3 双向链表尾插

//尾插
void ListPushBack(ListNode* pHead,LTDataType x)
{
	assert(pHead);
	ListNode* newnode = ListbuyNode(x);
	//newnode
	newnode->next = pHead;
	newnode->prev = pHead->prev;

	//pHead pHead->prev
	pHead->prev->next = newnode;
	pHead->prev = newnode;
}

4.4 双向链表尾删

//尾删
void ListPopBack(ListNode* pHead)
{
	assert(!LTEmpty(pHead));
	ListNode* del = pHead->prev;
	//pHead del->prev del
	del->prev->next = pHead;
	pHead->prev = del->prev;
	//释放
	free(del);
	del = NULL;
}

4.5 双向链表头插

//头插
void ListPushFront(ListNode* pHead,LTDataType x)
{
	ListNode* newnode = ListbuyNode(x);
	//newnode
	newnode->next = pHead->next;
	newnode->prev = pHead;

	//pHead newnode pHead->next
	pHead->next->prev = newnode;
	pHead->next = newnode;
}

4.6 双向链表头删

//头删
void ListPopFront(ListNode* pHead)
{
	assert(!LTEmpty(pHead));
	ListNode* del = pHead->next;
	//pHead->del
	pHead->next = del->next;
	//del-pHead
	del->prev = pHead;
	free(del);
	del = NULL;
}

4.7 双向链表寻找值

//查询
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* pcur = pHead->next;
	while (pcur != pHead)
	{
		if (pcur->Data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

4.8 双向链表任意插(之前、之后)

//任意之前插
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* mid = ListbuyNode(x);
	//mid
	mid->prev = pos->prev;
	mid->next = pos;
	//pos->prev mid pos
	pos->next->prev = mid;
	pos->prev->next = mid;
}


//任意之后插
void ListAfter(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* mid = ListbuyNode(x);
	//mid
	mid->prev = pos;
	mid->next = pos->next;
	//pos mid pos->next
	pos->next->prev = mid;
	pos->next = mid;
}

4.9 双向链表任意删

//任意删
void ListErase(ListNode* pos)
{
	assert(!LTEmpty(pos));
	ListNode* del = pos;
	//del->prev --- del->next
	del->prev->next = del->next;
	//del->next->prev --- del->prev
	del->next->prev = del->prev;
	free(pos);
	pos = NULL;
}

六、总结与反思(单双链表优缺点)

对于单链表的优缺点

对于双向链表优缺点 

双向链表优点 缺点
查询比单链表效率高,虽然都是o(N),但是可以利用双向特性,进行左右开始查询(类似二分法) 内存占用大(两个指针和一个数据,3个元素)
双向遍历 代码实现向较为单链表复杂
插入删除方便

总结:对于双向链表和单链表,当需要频繁存入大量数据并查询时,可以首先考虑单链表(内存和代码实现)。当需要频繁插入和删除并频繁遍历时,可以考虑双向链表(时间效率高)。


网站公告

今日签到

点亮在社区的每一天
去签到