03/20 数据结构之单链表的增删查改

文章介绍了单链表的基本操作,如尾插、尾删、头插、头删、查找和修改,并对比了顺序表与链表的特点,强调了链表在内存管理和缓存命中率方面的差异。同时,文章还讨论了在不同位置插入和删除节点的方法,并提到了CPU的三级缓存系统对数据访问速度的影响。
摘要由CSDN通过智能技术生成

单链表的增删查改

在这里插入图片描述

22/07/20,这篇记录自己的学习历程吧,这篇没涉及到思维层面的东西,就不用过多的文字就描述,注释写在代码旁边了,希望将来的我能看的懂

1.顺序表与链表特点:

顺序表:可动态增长的数组,数据在数组中存储必须是连续的。
优点:
1.可以随机访问。(排序,二分查找)
2.与链式结构对比,缓存命中率比较高。(本质由于物理空间连续)
缺点:
1.中间或者头部的插入很慢,需要挪动数据,时间复杂度是O(N)。
2.存在空间不够时,增容会有一定的消耗和空间浪费。

2.单链表的实现:

头文件SList.h的实现(各个接口函数):

分开放能让整个工程更有条理,更方便。
其中的函数分别是打印,尾插,尾删,头插,头删,查找及修改的声明。

#pragma once
#include <stdio.h>
#include <stdlib.h>
typedef int SListDataType;
//结点
typedef struct SListNode
{
	SListDataType data;
	struct SListNode* next;
}SListNode;

void SListPrint(SListNode* phead);

void SListPushBack(SListNode** pphead, SListDataType x);
void SListPopBack(SListNode** pphead);
void SListPushFront(SListNode** pphead, SListDataType x);
void SListPopFront(SListNode* phead);

SListNode* SListFind(SListNode* phead, SListDataType x);

SListNode* BuySListNode(SListDataType x);

void SListInsertAfter(SListNode* pos, SListDataType x)

源文件test.c的实现:

主要是要养成好的习惯,写的时候考虑多一点,否则经常出现内存问题,一写多一点代码程序就崩了。

申请一个新结点:
SListNode* BuySListNode(SListDataType x)
{
	SListNode* newNode = (SListNode*)malloc(sizeof(SListNode)); // 为新结点分配内存,并将其强制转换为SListNode*类型
	if (newNode == NULL) // 检查内存分配是否成功
	{
		printf("申请结点失败\n"); // 打印错误信息
		exit(-1); // 以错误码终止程序
	}
	newNode->data = x; // 将参数x赋值给新结点的数据域
	newNode->next = NULL; // 将新结点的指针域设为NULL

	return newNode; // 返回新结点的指针
}
打印一个单链表:
void SListPrint(SListNode* phead)
{
	SListNode* cur = phead;
	while (cur != NULL)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
尾插:
//注意!此处需要二级指针!才可以改变函数外的一级指针的值
void SListPushBack(SListNode** pphead, SListDataType x)
{
	SListNode* newNode = BuySListNode(x); // 调用BuySListNode函数,创建一个新结点
	//如果一开始就为空链表
	if (*pphead == NULL) // 检查链表是否为空
	{
		*pphead = newNode; // 如果为空,直接将新结点作为头结点
	}
	else // 如果不为空
	{
		//首先要找到尾巴
		SListNode* tail = *pphead; // 定义一个指针,指向头结点
		while (tail->next != NULL) // 循环遍历链表,直到找到尾结点
		{
			tail = tail->next; // 指针后移
		}
		tail->next = newNode; // 将尾结点的指针域指向新结点
	}
}
尾删:
void SListPopBack(SListNode** pphead)
{
	//1.空
	//2.一个结点
	//3.一个以上结点
	if (*pphead == NULL) // 检查链表是否为空
	{
		return; // 如果为空,直接返回
	}
	else if ((*pphead)->next == NULL) // 检查链表是否只有一个结点
	{
		free(*pphead); // 如果是,释放头结点的内存
		*pphead = NULL; // 将头指针设为NULL
	}
	else // 如果链表有多个结点
	{
		SListNode* prev = NULL; // 定义一个指针,指向尾结点的前一个结点
		SListNode* tail = *pphead; // 定义一个指针,指向尾结点
		while (tail->next != NULL) // 循环遍历链表,直到找到尾结点
		{
			prev = tail; // 指针后移
			tail = tail->next; // 指针后移
		}
		free(tail); // 释放尾结点的内存
		prev->next = NULL; // 将前一个结点的指针域设为NULL
	}
}
头插:
void SListPushFront(SListNode** pphead, SListDataType x)
{
	SListNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}
头删:
void SListPopFront(SListNode** pphead)
{
	//1.空
	//2.一个节点 + 一个以上节点
	if (*pphead = NULL)
	{
		return;
	}//如果没有这一段代码下一段可能会出错
	else
	{
		SListNode* next = (*pphead)->next;//先存储再释放
		free(*pphead);

		*pphead = next;
	}
}
查找修改:

在单链表中只要找到了数值,即可找到所对应的地址,利用地址可直接将数值修改,所以在这里查找修改放在一起。不过查找的代码段需要放在SList.c的源文件,修改的代码段需要放在test.c的源文件。

查找:
SListNode* SListFind(SListNode* phead, SListDataType x)
{
	SListNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
修改:

假设要查找3,并且把3改成30:

	SListNode* pos = SListFind(pList, 3);
	if (pos)
	{
		pos->data = 30;
	}

拓展:

例如对于删除功能来说,想删除这个位置的节点,需要找到前一个节点,用双向链表更方便。所以一般用链表存数据,比较少用单链表,更多的是用双向链表。但是Oj题几乎都是单链表,而且后面的哈希和图的临接表都要用到单链表,也不是一无是处啦。
所以在这里由于单链表的缺陷都是删除位置之后的节点。

pos位置之后插入:
void SListInsertAfter(SListNode* pos, SListDataType x)
{
	assert(pos);

	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}
pos位置之后删除x:
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	if (pos->next)
	{
		SListNode* next = pos->next;
		SListNode* nextnext = next->next;
		pos->next = nextnext;

		free(next);
	}
}
战损配图:

请添加图片描述

3.补充知识(存储体系):

CPU进行计算需要拿到数据,数据存放在内存当中,内存的速度有点跟不上CPU,这时候有L1,L2,L3的三级缓存,都比内存快,比三级缓存更快一点的是寄存器,越快的越贵,越慢的空间越大。缓存命中率实际上是由系统的预加载机制导致的,当是顺序表的时候,预加载的刚好就是想要的,是链表的时候,预加载的可能是你想要的但是大部分都是你不想要的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值