当前位置: 首页 > article >正文

数据结构之链表(单链表)

目录

一、链表的概念

二、链表的分类

三、单链表的实现

1. 创建新的节点

2. 打印链表

3. 链表的头插和尾插

尾插:要注意第一次插入时链表为空的情况。

头插:

4. 单链表的头删和尾删

尾删:注意链表中只有一个元素的情况。且要保存尾节点的前一个节点。

头删:

5. 单链表的查找


一、链表的概念

        链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表

中的 指针链接 次序实现的 。链表实际上就像一列火车一样,每一个节点都是火车的一节车厢。

 

注意:1.从图上可以看出,链式结构在逻辑上是是连续的,但是在物理上不一定连续

           2.现实中的节点一般都是从堆上申请出来的。

           3.从堆上申请的空间是按照一定的策略来分配的,连续两次申请的空间可能连续也可能不连续。 

二、链表的分类

        从严格上来讲,链表有:单向/双向, 带头/不带头(哨兵位头节点), 循环/不循环 这些形式,这些形式组合起来共有8种结构,但是本文章只会讲两种基本链表:单向不带头不循环(以下简称单链表)和双向带头循环链表(以下简称双向链表)。掌握以上两种链表的结构以后剩下的结构也就顺其自然的掌握了。

三、单链表的实现

        我们要知道单链表是一种数据结构,而提到数据结构,无非就是增删查改这些功能 。

以下是单链表的相关声明。

// 1、无头+单向+非循环链表增删查改实现
typedef int SLTDateType;
typedef struct SListNode
{
 SLTDateType data;
 struct SListNode* next;
}SListNode;
// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
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);

        在进行增删查改之前,我们先写两个函数,一个是创建新节点,一个是打印链表从而观察操作是否实现。 

1. 创建新的节点

        如链表的概念所说,链表的节点是从堆上申请空间,所以我们要使用malloc函数来开辟一块空间,并将值存入data中。

// 动态申请一个结点
SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	// 检查开辟空间是否成功
	if (newnode == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	newnode->data = x;
	// 节点的next置空防止野指针
	newnode->next = NULL;
	return newnode;
}

2. 打印链表

// 单链表打印
void SListPrint(SListNode* plist)
{
	// 定义一个指针来遍历链表
	SListNode* cur = plist;
	while (cur)
	{
		// 遍历过程
		printf("%d->",cur->data);
		cur = cur->next;
	}
	printf("NULL");
}

3. 链表的头插和尾插

尾插:要注意第一次插入时链表为空的情况。

// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	// 因为要对链表进行修改,所以我们要使用二级指针
	SListNode* head = *pplist;
	if (head == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		// 因为是尾插,所以要先找到尾
		// 找尾
		SListNode* tail = head;
		while (tail->next)
		{
			tail = tail->next;
		}
		// 尾插
		tail->next = newnode;
	}
	
}

头插:

// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	SListNode* newnode = BuySListNode(x);
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	newnode->next = *pplist;
	*pplist = newnode;
}

4. 单链表的头删和尾删

尾删:注意链表中只有一个元素的情况。且要保存尾节点的前一个节点。

// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
    assert(*pplist);
	SListNode* phead = *pplist;
	if (phead->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* tail = phead;
		SListNode* prev = NULL;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		tail = NULL;
		prev->next = NULL;
	}
}

头删:

// 单链表头删
void SListPopFront(SListNode** pplist)
{
	assert(*pplist);
	SListNode* phead = *pplist;
	if (phead->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* newHead = phead->next;
		free(phead);
		phead = NULL;
		*pplist = newHead;
	}
}

5. 单链表的查找

// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* cur = plist;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

单链表的修改就是在单链表的查找的基础上实现的,所以这里不会进行过多赘述。


http://www.kler.cn/a/593173.html

相关文章:

  • 函数的介绍
  • QtitanRibbon在医疗场景中的精细化功能应用
  • ollama不安装到c盘,安装到其他盘
  • (2025|ICLR|华南理工,任务对齐,缓解灾难性遗忘,底层模型冻结和训练早停)语言模型持续学习中的虚假遗忘
  • 免费实用工具,wps/office/永中通吃!
  • oracle 基础知识事务的特性
  • Tomcat 与 Java 环境变量配置简明教程
  • PHP序列化漏洞
  • Python散点图(Scatter Plot):高阶分析、散点图矩阵、三维散点图及综合应用
  • 信息检索 information retrieval--lab练习(更新中)
  • Linux信号的处理
  • 【蓝桥杯速成】| 7.01背包练习生
  • 第5章:Docker镜像管理实战:构建、推送与版本控制
  • 【人工智能-前端OpenWebUI】--图片显示
  • 详细介绍GetDlgItem()
  • 器材借用管理系统详细设计基于Spring Boot-SSM
  • 汽车电子硬件架构 --- 车用电子芯片与处理器架构的技术演进及核心价值
  • 深入解析 DAI 与 SAI:Linux 音频驱动中的核心概念
  • 【设计模式有哪些】
  • Linux | gcc编译篇