刨析数据结构(二)

🌈个人主页:小田爱学编程

🔥 系列专栏:数据结构------------"带你无脑刨析"

🏆🏆关注博主,随时获取更多关于数据结构的优质内容!🏆🏆


😀欢迎来到小田代码世界~

😁 喜欢的小伙伴记得一键三连哦 ૮(˶ᵔ ᵕ ᵔ˶)ა


一.线性表的链式储存

链表:线性表的链式储存方式,逻辑结构不一定连续,物理结构不一定连续

描述:由数据域和指针域组成

🌏头结点:点是为了操作方便而设立的,放在第一个元素结点之前,不保存任何有意义的数据

🌏头节点:即为指向第一个节点的地址

🔥链表分类:八种

👨‍🚀 以单链表(不带头单向不循环链表)

二.单链表

1.优缺点

🔥任意位置插入删除,时间复杂度

🔥没有增容问题,插入一个开辟一个空间

🔥不支持随机访问

2.创建

cs 复制代码
//定义链表
typedef int SLTDataType;//数值域
//链表是由节点组成
typedef struct SListNode
{
	SLTDataType data;//int data
	struct  SListNode* next;//它用来存储当前节点的下一个节点的地址
}SLTNode;//typedef struct SListNode SLTNode;

3.打印

cs 复制代码
void SLTPrint(SLTNode* phead) {
	SLTNode* pcur = phead;
	while (pcur)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}
SLTNode* plist = node1;
SLTPrint(plist);

3.申请空间

cpp 复制代码
SLTNode* SLTBuyNode(SLTDataType x) {
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL) {
		perror("malloc fail!");
		exit(1);
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}

4.增加元素

尾插:

cpp 复制代码
void SLTPushBack(SLTNode** pphead, SLTDataType x) {
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);

	//链表为空,新节点作为phead
	if (*pphead == NULL) {
		*pphead = newnode;
		return;
	}
	//链表不为空,找尾节点
	SLTNode* ptail = *pphead;
	while (ptail->next)
	{
		ptail = ptail->next;
	}
	//ptail就是尾节点
	ptail->next = newnode;
}

头插:

cs 复制代码
void SLTPushFront(SLTNode** pphead, SLTDataType x) {
	assert(pphead);
	SLTNode* newnode = SLTBuyNode(x);

	//newnode *pphead
	newnode->next = *pphead;
	*pphead = newnode;
}

在指定位置插入

cs 复制代码
//在指定位置之前插入数据
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {
	assert(pphead);
	assert(pos);
	//要加上链表不能为空
	assert(*pphead);

	SLTNode* newnode = SLTBuyNode(x);
	//pos刚好是头结点
	if (pos == *pphead) {
		//头插
		SLTPushFront(pphead, x);
		return;
	}

	//pos不是头结点的情况
	SLTNode* prev = *pphead;
	while (prev->next != pos)
	{
		prev = prev->next;
	}
	//prev -> newnode -> pos
	prev->next = newnode;
	newnode->next = pos;
}

5.删除元素

尾删

cs 复制代码
void SLTPopBack(SLTNode** pphead) {
	assert(pphead);
	//链表不能为空
	assert(*pphead);
	//链表不为空
	//链表只有一个节点,有多个节点
	if ((*pphead)->next == NULL) {
		free(*pphead);
		*pphead = NULL;
		return;
	}
	SLTNode* ptail = *pphead;
	SLTNode* prev = NULL;
	while (ptail->next)
	{
		prev = ptail;
		ptail = ptail->next;
	}
	prev->next = NULL;
	//销毁尾结点
	free(ptail);
	ptail = NULL;
}

头删

cs 复制代码
void SLTPopFront(SLTNode** pphead) {
	assert(pphead);
	//链表不能为空
	assert(*pphead);
	//让第二个节点成为新的头
	//把旧的头结点释放掉
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

🔥指定位置删除:注意删除的逻辑

cs 复制代码
//删除pos节点
void SLTErase(SLTNode** pphead, SLTNode* pos) {
	assert(pphead);
	assert(*pphead);
	assert(pos);

	//pos刚好是头结点,没有前驱节点,执行头删
	if (*pphead == pos) {
		//头删
		SLTPopFront(pphead);
		return;
	}

	SLTNode* prev = *pphead;
	while (prev->next != pos)
	{
		prev = prev->next;
	}
	//prev pos pos->next
	prev->next = pos->next;
	free(pos);
	pos = NULL;
}
void SLTEraseAfter(SLTNode* pos) {
	assert(pos);
	//pos->next不能为空
	assert(pos->next);

	//pos  pos->next  pos->next->next
	SLTNode* del = pos->next;
	pos->next = pos->next->next;
	free(del);
	del = NULL;
}

6.修改元素

cpp 复制代码
//给一个数据,找到这个数据所在的节点,并用新数据修改
void SListChangeDate(SLTNode*pphead, SLTDataType x, SLTDataType y)
//不需要改变节点的地址,所以值传递即可 
//x是查找的数据,y是新的数据,用来修改查找的数据                                              
{
	SLTNode*cru = pphead;
	while (cru != NULL)//如果没有节点,根本不会进入循环去找
	{
		if (cru->data == x)
		{
			cru->data = y;
			break;//修改完数据后,就跳出循环
		}
		else
		{
			cru = cru->next;
		}
	}
	if (cru == NULL)//如果循环完单链表,没有找到要修改的那个数据
	{
		printf("要修改的数据不存在,请重新修改数据\n");
	}
	else
	{
		printf("修改成功\n");
	}
}

7.查找元素

cs 复制代码
SLTNode* SLTFind(SLTNode** pphead, SLTDataType x) {
	assert(pphead);
	//遍历链表 
	SLTNode* pcur = *pphead;
	while (pcur) //pcur != NULL
	{
		if (pcur->data == x) {
			return pcur;
		}
		pcur = pcur->next;
	}
	//没有找到
	return NULL;
}

8.销毁链表

cs 复制代码
void SListDesTroy(SLTNode** pphead) {
	assert(pphead);
	assert(*pphead);

	SLTNode* pcur = *pphead;
	while (pcur)
	{
		SLTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	*pphead = NULL;
}

三.双向链表

1.注意

🔥带头双向循环链表

🔥当链表中只要头节点的时候,为空链表

🔥头节点是不能删除的,指向可以改变

🔥不需要改变头节点的指向,不需要传二级指针

🔥二级指针对实参会产生影响

2.创建

cs 复制代码
typedef int LTDataType;
typedef struct ListNode {
	LTDataType data;
	struct ListNode* prev;
	struct ListNode* next;
}LTNode;

3.打印

cs 复制代码
LTNode* LTInit() {
	LTNode* phead = LTBuyNode(-1);
	return phead;
}

4.增加元素

尾插**(不需要找尾操作**

cs 复制代码
//尾插
void LTPushBack(LTNode* phead, LTDataType x) {
	assert(phead);
	LTNode* newnode = LTBuyNode(x);
	//phead phead->prev(ptail)  newnode
	newnode->next = phead;
	newnode->prev = phead->prev;

	phead->prev->next = newnode;
	phead->prev = newnode;
}

头插

cs 复制代码
//头插
void LTPushFront(LTNode* phead, LTDataType x) {
	assert(phead);

	LTNode* newnode = LTBuyNode(x);
	//phead newnode phead->next
	newnode->next = phead->next;
	newnode->prev = phead;

	phead->next->prev = newnode;
	phead->next = newnode;
}

任意位置插入

cs 复制代码
//在pos位置之后插入数据
void LTInsert(LTNode* pos, LTDataType x) {
	assert(pos);
	LTNode* newnode = LTBuyNode(x);
	//pos newnode pos->next
	newnode->next = pos->next;
	newnode->prev = pos;

	pos->next->prev = newnode;
	pos->next = newnode;
}
//删除pos位置的数据
void LTErase(LTNode* pos) {
	assert(pos);

	//pos->prev pos  pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

5.删除元素

头删

cs 复制代码
void LTPopFront(LTNode* phead) {
	assert(phead);
	assert(phead->next != phead);

	LTNode* del = phead->next;
	LTNode* next = del->next;

	//phead del next
	next->prev = phead;
	phead->next = next;

	free(del);
	del = NULL;
}

尾删

cs 复制代码
void LTPopBack(LTNode* phead) {
	assert(phead);
	//链表为空:只有一个哨兵位节点
	assert(phead->next != phead);

	LTNode* del = phead->prev;
	LTNode* prev = del->prev;

	prev->next = phead;
	phead->prev = prev;

	free(del);
	del = NULL;
}

任意位置删除

cs 复制代码
void LTErase(LTNode* pos) {
	assert(pos);

	//pos->prev pos  pos->next
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;

	free(pos);
	pos = NULL;
}

6.修改元素

cs 复制代码
void DeleteNode(LTNode** pHead, LTNode* toBeDeleted) {
    if (pHead == NULL || toBeDeleted == NULL) {
        return;
    }

    LTNode* head = *pHead;

    // 要删除的节点是头节点
    if (head == toBeDeleted) {
        *pHead = toBeDeleted->next;
    }
    
    // 调整前驱节点的next指针
    if (toBeDeleted->prev != NULL) {
        toBeDeleted->prev->next = toBeDeleted->next;
    }
    
    // 调整后继节点的prev指针
    if (toBeDeleted->next != NULL) {
        toBeDeleted->next->prev = toBeDeleted->prev;
    }
    
    free(toBeDeleted);
}

7.查找元素

cs 复制代码
LTNode* LTFind(LTNode* phead, LTDataType x) {
	assert(phead);
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x) {
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}

8.销毁链表

cs 复制代码
void LTDesTroy(LTNode* phead) {
	//哨兵位不能为空
	assert(phead);

	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	//链表中只有一个哨兵位
	free(phead);
	phead = NULL;
}

🥇这是博主年前的最后一篇文章哦,年后再继续更新,年后会开启新的刷题专栏!😊

🥇还有不到不到一周就过年喽!祝大家新的一年财源滚滚,学习进步,身体健康,咱们明年见!

🎁🎁🎁今天的分享到这里就结束啦!如果觉得文章还不错的话,可以三连支持一下,您的支持就是我前进的动力!

相关推荐
legend_jz3 分钟前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE12 分钟前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++
莫叫石榴姐24 分钟前
数据科学与SQL:组距分组分析 | 区间分布问题
大数据·人工智能·sql·深度学习·算法·机器学习·数据挖掘
bluefox197938 分钟前
使用 Oracle.DataAccess.Client 驱动 和 OleDB 调用Oracle 函数的区别
开发语言·c#
ö Constancy1 小时前
c++ 笔记
开发语言·c++
fengbizhe1 小时前
笔试-笔记2
c++·笔记
徐霞客3201 小时前
Qt入门1——认识Qt的几个常用头文件和常用函数
开发语言·c++·笔记·qt
fpcc1 小时前
redis6.0之后的多线程版本的问题
c++·redis
茶猫_1 小时前
力扣面试题 - 25 二进制数转字符串
c语言·算法·leetcode·职场和发展
螺旋天光极锐斩空闪壹式!2 小时前
自制游戏:监狱逃亡
c++·游戏