单链表和双向链表

目录

目录

目录

一、链表种类

二、单链表概念

三、单链表实现

[3.1 单链表创建结点](#3.1 单链表创建结点)

[3.2 单链表销毁](#3.2 单链表销毁)

[3.3 单链表尾插](#3.3 单链表尾插)

[3.4 单链表尾删](#3.4 单链表尾删)

[3.5 单链表头插](#3.5 单链表头插)

[3.6 单链表头删](#3.6 单链表头删)

[3.7 单链表寻找值](#3.7 单链表寻找值)

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

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

四、双向链表概念

五、双向链表实现

[4.1 双向链表创建结点](#4.1 双向链表创建结点)

[4.2 双向链表销毁](#4.2 双向链表销毁)

[4.3 双向链表尾插](#4.3 双向链表尾插)

[4.4 双向链表尾删](#4.4 双向链表尾删)

[4.5 双向链表头插](#4.5 双向链表头插)

[4.6 双向链表头删](#4.6 双向链表头删)

[4.7 双向链表寻找值](#4.7 双向链表寻找值)

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

[4.9 双向链表任意删](#4.9 双向链表任意删)

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


一、链表种类

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

二、单链表概念

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

三、单链表实现

3.1 单链表创建结点

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

cpp 复制代码
//结构体
typedef int SLTDataType;
typedef struct SListNode
{
    SLTDataType data;
    struct SListNode* next;
}SLTNode;

创建结点

cpp 复制代码
//创造节点
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 单链表销毁

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

3.3 单链表尾插

cpp 复制代码
//尾插
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 单链表尾删

cpp 复制代码
//尾删
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 单链表头插

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

3.6 单链表头删

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

3.7 单链表寻找值

cpp 复制代码
//查询
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 单链表任意插(之前、之后)

cpp 复制代码
//任意位置之前插入
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 单链表任意删(当前、后面)

cpp 复制代码
//任意删
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 双向链表创建结点

定义双向链表

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

创建头结点

cpp 复制代码
//创建头结点
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;
}

创建结点

cpp 复制代码
//创造新节点
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 双向链表销毁

cpp 复制代码
//销毁
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 双向链表尾插

cpp 复制代码
//尾插
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 双向链表尾删

cpp 复制代码
//尾删
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 双向链表头插

cpp 复制代码
//头插
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 双向链表头删

cpp 复制代码
//头删
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 双向链表寻找值

cpp 复制代码
//查询
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 双向链表任意插(之前、之后)

cpp 复制代码
//任意之前插
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 双向链表任意删

cpp 复制代码
//任意删
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个元素) |
| 双向遍历 | 代码实现向较为单链表复杂 |
| 插入删除方便 | |

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

相关推荐
杰克尼3 小时前
BM5 合并k个已排序的链表
数据结构·算法·链表
xiaolang_8616_wjl4 小时前
c++文字游戏_闯关打怪
开发语言·数据结构·c++·算法·c++20
hqxstudying4 小时前
Java创建型模式---单例模式
java·数据结构·设计模式·代码规范
sun0077004 小时前
数据结构——栈的讲解(超详细)
数据结构
ゞ 正在缓冲99%…9 小时前
leetcode918.环形子数组的最大和
数据结构·算法·leetcode·动态规划
Orlando cron12 小时前
数据结构入门:链表
数据结构·算法·链表
许愿与你永世安宁16 小时前
力扣343 整数拆分
数据结构·算法·leetcode
Heartoxx17 小时前
c语言-指针(数组)练习2
c语言·数据结构·算法
杰克尼18 小时前
1. 两数之和 (leetcode)
数据结构·算法·leetcode