【数据结构入门指南】单链表

这篇具有很好参考价值的文章主要介绍了【数据结构入门指南】单链表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述:

 由于顺序表插入和删除元素需要移动大量数据,导致运行效率下降。因此引入了另一种数据结构 —— 链表。链表又分为单链表和双链表。单链表结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。


一. 单链表的定义

 单链表通过一组任意的存储单元来存储线性表中的数据元素,不需要使用地址连续的存储单元,因此它不要求在逻辑上相邻的两个元素在物理位置上也相邻。

构成:

 单链表由一系列节点组成,每个节点包含两个部分:数据域(存储数据元素)和指针域(存储下一个节点地址)。

typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType date;//数据域
	struct SListNode* next;//指针域
}SLTNode;

特点:

  1. 单链表的节点是离散分布在内存中的,通过指针将它们串联起来。
  2. 单链表可以动态地分配内存空间,可以根据需要灵活地插入、删除节点。
    【数据结构入门指南】单链表,数据解构和C++学习分享,数据结构,学习,c++,c语言

二. 单链表的创建

typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType date;//数据域
	struct SListNode* next;指针域
}SLTNode;

首先创建一个结构体struct SListNode用来存储数据和指针。
考虑到后续数据类型修改的方便性,我们将struct SListNode 用typedef重命名为SLNode
同时为方便以后调用接口实现不同数据类型链接,我们将数据域的类型int重命名为SLDateType。(后续存储不停数据只需修改此处即可)


三、单链表各种接口实现

1. 动态申请一个结点

 后续要插入新节点时,首先要创建一个节点来存储相关信息,在连接到单链表合适位置。

代码实现:

SLTNode* BuySListNode(SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	newnode->date = x;
	newnode->next = NULL;
	return newnode;
}

2. 单链表打印

打印单链表,我们只需记录头节点,然后遍历访问,依次打印即可。

代码实现:

void SLTPrint(SLTNode* phead)
{ 
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->date);
		cur = cur->next;
	}
	printf("NULL\n");
}

3. 尾插

尾插分两种情况:没有节点和有节点。
①:没有节点:创建一个新节点,然后头指针指向新节点。
②:有节点:遍历找到最后一个节点,然后将其下一个节点指向新节点

代码实现:

//由于尾插第一种情况需要改变结构体指针,所以我们要传结构体二级指针
void SLTPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	if (*pphead == NULL)//没有节点
	{
		*pphead = newnode;
	}
	else
	{
	    //有节点
		SLTNode* tail = *pphead;
		while (tail->next)//遍历找到最后一个节点
		{
			tail = tail->next;
		}
		//尾插
		tail->next = newnode;
	}
}

4. 头插

头插就相对简单。不管原链表有无节点,只需插入新节点即可。

代码实现:

//由于头插会改变头指针,所以我们传二级指针
void SLTPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);//新节点
	//头插
	newnode->next = *pphead;
	*pphead = newnode;
}

5. 尾删

尾删分3中情况:
①:首先要判断链表中是否还有节点可删。
②:链表中只有一个节点。一个节点比较简单,将头指针置空,然后释放头节点即可。
③:链表中有多个节点。多个节点,首先要遍历找到尾节点的前一个节点。然后将其指针域置空,并释放尾节点。

代码实现:

void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	//1.空
	assert(*pphead);

	if ((*pphead)->next == NULL)//2.一个节点
	{
		(*pphead)->next = NULL;
	}
	else
	{
		//3.多个节点
		SLTNode* tail = *pphead;
		遍历找到尾节点的前一个节点
		while (tail->next->next)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}

}

6. 头删

头删分两种情况:
①:首先判断链表中是否还有节点可删。
②:链表还有节点可删。首先保存第二个节点,在释放头节点。并将头指针指向第二个节点。

代码实现:

void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);
	//空
	assert(*pphead);
	//非空
	SLTNode* newnode = (*pphead)->next;//保存第二个节点
	free(*pphead);//释放头节点
	*pphead = newnode;
}

7、查找

查找和打印一样,直接遍历访问即可。找到了返回地址,没有找打返回空指针。

代码实现:

SLTNode* SLTFind(SLTNode* phead, SLTDateType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->date == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

8、在pos之前插入x

链表中,不管是单链表还是双链表在某处插入新节点,一般默认前插。
前插分两种情况:
①:pos位置为第一个节点。可以复用前面头插接口实现。
②: 遍历访问链表找到pos前一个节点prev,然后将pos、prev、新节点连接起来。

代码实现:

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(*pphead);
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);//头插
	}
	else
	{
		SLTNode* prev = *pphead;
		//遍历找到pos前一个节点
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySListNode(x);
		//prev,newnode,pos三个节点链接
		prev->next = newnode;
		newnode->next = pos;
	}
}

9、在pos后插入x

在pos之前插入x,相对来所比较复杂。所以如果没有特殊要求,可以采用pos后插。所以我们在这提供pos后插接口。

代码实现:

void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);

	SLTNode* newnode = BuySListNode(x);
	//后插
	newnode->next = pos->next;
	pos->next = newnode;
}

10、删除pos位置的值

删除pos位置的值一样分两种情况:
①:如果pos为头节点,复用头删接口。
②:遍历找到pos前一个节点prev。然后将pos前一个节点和后一个节点链接起来,并释放pos即可。

代码实现:

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		//free(pos);
	}
}

11、删除pos位置之后的值

要删除pos位置之后的值,我们首先将pos和pos后两个节点指针链接起来,并释放pos后一个节点即可。(要判断pos是否为尾节点)

代码实现:

void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	//检查pos是否为尾节点
	assert(pos->next);

	SLTNode* posNext = pos->next;
	pos->next = posNext->next;

	free(posNext);
	posNext = NULL;
}

12、销毁

代码实现:

void SLTDestory(SLTNode** pphead)
{
	assert(pphead);

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

四、所有代码展示

List.h:

#pragma once

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>


typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType date;
	struct SListNode* next;
}SLTNode;


void SLTPrint(SLTNode* phead);

SLTNode* BuySListNode(SLTDateType x);

void SLTPushBack(SLTNode** pphead, SLTDateType x);
void SLTPushFront(SLTNode** pphead, SLTDateType x);
void SLTPopBack(SLTNode** pphead);
void SLTPopFront(SLTNode** pphead);

SLTNode* SLTFind(SLTNode* phead, SLTDateType x);

//在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x);
//在pos之后插入x
void SLTInsertAfter(SLTNode* pos, SLTDateType x);
//删除pos位置的值
void SLTErase(SLTNode** pphead, SLTNode* pos);
//删除pos位置之后的值
void SLTEraseAfter(SLTNode* pos);

//销毁
void SLTDestory(SLTNode** pphead)

List.h:

include "SList.h"


void SLTPrint(SLTNode* phead)
{ 
	SLTNode* cur = phead;
	while (cur)
	{
		printf("%d->", cur->date);
		cur = cur->next;
	}
	printf("NULL\n");
}


SLTNode* BuySListNode(SLTDateType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	if (newnode == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	newnode->date = x;
	newnode->next = NULL;
	return newnode;
}


void SLTPushBack(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}


void SLTPushFront(SLTNode** pphead, SLTDateType x)
{
	assert(pphead);
	SLTNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}


void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	//1.空
	assert(*pphead);

	//2.一个节点
	//3.多个节点
	if ((*pphead)->next == NULL)
	{
		(*pphead)->next = NULL;
	}
	else
	{
		SLTNode* tail = *pphead;
		while (tail->next->next)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}

}


void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);
	//空
	assert(*pphead);
	//非空
	SLTNode* newnode = (*pphead)->next;
	free(*pphead);
	*pphead = newnode;
}

SLTNode* SLTFind(SLTNode* phead, SLTDateType x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->date == x)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDateType x)
{
	assert(pphead);
	assert(*pphead);
	if (*pphead == pos)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		SLTNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}



void SLTInsertAfter(SLTNode* pos, SLTDateType x)
{
	assert(pos);

	SLTNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (*pphead == pos)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		//free(pos);
	}
}


void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);
	//检查pos是否为尾节点
	assert(pos->next);

	SLTNode* posNext = pos->next;
	pos->next = posNext->next;

	free(posNext);
	posNext = NULL;
}

void SLTDestory(SLTNode** pphead)
{
	assert(pphead);

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

【数据结构入门指南】单链表,数据解构和C++学习分享,数据结构,学习,c++,c语言
【数据结构入门指南】单链表,数据解构和C++学习分享,数据结构,学习,c++,c语言文章来源地址https://www.toymoban.com/news/detail-632273.html

到了这里,关于【数据结构入门指南】单链表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 数据结构入门指南:二叉树

    数据结构入门指南:二叉树

    目录 文章目录 前言  1. 树的概念及结构    1.1 树的概念  1.2 树的基础概念 1.3 树的表示 1.4 树的应用  2. 二叉树 2.1 二叉树的概念  2.2 二叉树的遍历         在计算机科学中,数据结构是解决问题的关键。而二叉树作为最基本、最常用的数据结构之一,不仅在算法和数据

    2024年02月12日
    浏览(10)
  • C语言笔记 | 数据结构入门指南

    文章目录 0x00 前言 0x01 百鸡百钱 0x1 题目描述 0x2 问题分析 0x3 代码设计 0x4 完整代码 0x5 运行效果 0x6 举一反三 [兔鸡百钱] 0x02 借书方案知多少 0x1 题目描述 0x2 问题分析 0x3 代码设计 0x4 完整代码 0x5 运行效果 0x6 举一反三 [领导小组方案] 0x03 打鱼还是晒网 0x1 题目描述 0x2 问题分

    2024年02月08日
    浏览(12)
  • Python基础数据结构入门必读指南

    Python基础数据结构入门必读指南

    作者主页:涛哥聊Python 个人网站:涛哥聊Python 大家好,我是涛哥,今天为大家分享的是Python中常见的数据结构。 含义:数组是一种有序的数据结构,其中的元素可以按照索引来访问。数组的大小通常是固定的,一旦创建就不能更改。 基本操作: 含义:列表是Python中内置的

    2024年02月07日
    浏览(33)
  • 数据结构入门指南:带头双向循环链表

    数据结构入门指南:带头双向循环链表

    目录 文章目录 前言 1.结构与优势 2.链表实现       2.1 定义链表 2.2 创建头节点 2.3 尾插 2.4 输出链表 2.5 尾删 2.6 头插 2.7头删 2.8 节点个数 2.9 查找 2.10 位置插入 2.11 位置删除 2.12 销毁链表  3. 源码 总结         链表一共有8种结构,但最常用的就是无头单向链表、和带头

    2024年02月14日
    浏览(16)
  • 【数据结构入门指南】二叉树顺序结构: 堆及实现(全程配图,非常经典)

    【数据结构入门指南】二叉树顺序结构: 堆及实现(全程配图,非常经典)

    普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。 而完全二叉树更适合使用顺序结构存储。   现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储 ,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一

    2024年02月12日
    浏览(11)
  • 【数据结构入门指南】二叉树链式结构的实现(保姆级代码思路解读,非常经典)

    【数据结构入门指南】二叉树链式结构的实现(保姆级代码思路解读,非常经典)

    其他数据结构不同,二叉树的增删查改接口实现的意义不大(后续搜索树的增删查改才有意义)。普通初阶二叉树更重要的是学习控制结构,为后续的AVL树、红黑树等高级数据结构打下基础。同时大部分OJ题也出在此处。 所谓二叉树遍历(Traversal)是按照某种特定的规则,依次

    2024年02月11日
    浏览(14)
  • 【Java--数据结构】提升你的编程段位:泛型入门指南,一看就会!

    【Java--数据结构】提升你的编程段位:泛型入门指南,一看就会!

    泛型是一种编程概念,它允许我们编写可以适用于多种数据类型的代码。通过使用泛型,我们可以在编译时期将具体的 数据类型作为参数 传递给代码,从而实现代码 的复用和灵活性 。 在传统的编程中,我们通常需要为不同的数据类型编写不同的代码,这样会导致代码冗余

    2024年04月26日
    浏览(25)
  • 探索数据结构:单链表的实战指南

    探索数据结构:单链表的实战指南

    ✨✨ 欢迎大家来到贝蒂大讲堂✨✨ 🎈🎈养成好习惯,先赞后看哦~🎈🎈 所属专栏:数据结构与算法 贝蒂的主页:Betty‘s blog 在上一章节中我们讲解了数据结构中的顺序表,知道了顺序表的空间是连续存储的,这与数组非常类似,为我们随机访问数据提供了便利的条件。但

    2024年03月09日
    浏览(15)
  • 数据结构入门——单链表

    目录 前言 链表的定义 链表的创建 头插法创建链表 尾插法创建链表 链表的删除 在头部删除元素 在尾部删除元素 查找固定元素 在链表中插入元素 在pos位置前插入 在pos位置后插入 删除链表结点 删除pos位置的结点 删除pos后的结点 链表的销毁 写在最后 前面我们学习了顺序表

    2024年02月20日
    浏览(10)
  • 数据结构入门 — 链表详解_单链表

    数据结构入门 — 链表详解_单链表

    数据结构入门 — 单链表详解 * 博客主页链接:https://blog.csdn.net/m0_74014525 关注博主,后期持续更新系列文章 文章末尾有源码 *****感谢观看,希望对你有所帮助***** 第一篇:数据结构入门 — 链表详解_单链表 第二篇:数据结构入门 — 链表详解_双向链表 第三篇:数据结构入门

    2024年02月11日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包