数据结构——单链表的实现(c语言版)

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

前言 

        单链表作为顺序表的一种,了解并且熟悉它的结构对于我们学习更加复杂的数据结构是有一定意义的。虽然单链表有一定的缺陷,但是单链表也有它存在的价值, 它也是作为其他数据结构的一部分出现的,比如在图,哈希表中。

目录

1.链表节点的结构

2.头插头删

3.尾插尾删

4.任意位置的插入和删除

5.查找链表的值和修改链表节点的值

6.销毁链表

7.测试代码

8.全部代码

9.总结 


1.链表节点的结构

        单链表有节点的值和节点的next指针组成,如图:

数据结构——单链表的实现(c语言版),数据结构,c语言,开发语言 

typedef int SListDatatype;
typedef struct SListNode
{
	SListDatatype _data;//存储节点的数据
	struct SListNode* _next;
}SListNode;

2.头插头删

        头插分为两种情况,第一种是没有节点的情况,第二种是 有节点的情况。如图:

数据结构——单链表的实现(c语言版),数据结构,c语言,开发语言

                头删也分为两种情况,如果只有一个节点的时候,直接删除就行了,然后将头结点置空。如果有多个节点,需要先记录头结点,然后再进行删除就可以了。数据结构——单链表的实现(c语言版),数据结构,c语言,开发语言

void SListPushFront(SListNode** ppHead, SListDatatype data)//头插
{
	SListNode* newNode = SlistBuyNode(data);//申请一个新的节点
	if (*ppHead == NULL)
	{
		//链表为空
		*ppHead = newNode;
		return;
	}
	newNode->_next = (*ppHead);
	*ppHead = newNode;//对头结点进行链接
}
void SListPopFront(SListNode** ppHead)//头删
{
	assert(*ppHead);//确保指针的有效性
	if ((*ppHead)->_next == NULL)
	{
		//链表只有一个节点
		free(*ppHead);
		*ppHead = NULL;
		return;
	}
	//删除头结点,然后更新头结点
	SListNode* newHead = (*ppHead)->_next;
	free(*ppHead);
	*ppHead = newHead;
	return;
}

3.尾插尾删

        尾插也分为链表为空和指针不为空的情况,如果链表为空,申请节点,让链表的头结点指向申请的节点,然后将这个节点的_next置空,如果链表不为空,首先需要找到尾结点,然后将尾结点与这个节点链接起来,再将这个新申请的节点的_next置空。如图:

 数据结构——单链表的实现(c语言版),数据结构,c语言,开发语言

        尾删也分为两种情况:1只有一个节点和2存在多个节点

如果只有一个节点,删除以后需要将头结点置空,防止出现野指针的问题。

如果有多个节点,删除尾结点以后需要将新的尾结点置空。

如图:数据结构——单链表的实现(c语言版),数据结构,c语言,开发语言 

void SListPushBack(SListNode** ppHead, SListDatatype data)//尾插
{
	SListNode*newNode =  SlistBuyNode(data);//申请一个新的节点
	
	if (*ppHead == NULL)//链表为空
	{
		*ppHead = newNode;
		return;
	}
	if ((*ppHead)->_next == NULL)//链表只存在一个节点
	{
		(*ppHead)->_next = newNode;
		return;
	}
	SListNode* cur = *ppHead;
	while (cur->_next)//找到尾节点
	{
		cur = cur->_next;
	}
	cur->_next = newNode;//进行链接
	return;
}
void SListPopBack(SListNode** ppHead)//尾删
{
	assert(*ppHead);
	if (*ppHead == NULL)//链表为空不需要删除
	{
		return;
	}
	if ((*ppHead)->_next == NULL)
	{
		free(*ppHead);//链表只有一个节点
		(*ppHead) = NULL;
		return;
	}
	SListNode* cur = *ppHead;
	SListNode* prev = NULL;

	while (cur->_next)//找到尾结点
	{
		prev = cur;//保存上一个节点
		cur = cur->_next;
	}
	free(cur);//释放尾结点所在的空间
	prev->_next = NULL;//将上一个节点的_next置空
	return;

4.任意位置的插入和删除

        由于单链表结构的限制,这里只实现了在pos位置之后的插入和删除,如果删除pos的后一个节点就需要确保pos的后一个节点是存在的,否则就会出现问题。

void SListInsertAfter(SListNode*pos, SListDatatype data)//任意位置的插入,在pos之后插入
{
	assert(pos);//确保指针不为空
	SListNode* newNode = SlistBuyNode(data);
	SListNode* next = pos->_next;
	pos->_next = newNode;
	newNode->_next = next;
}
void SListErase(SListNode*pos)//任意位置的删除,pox位置之后的删除
{
	assert(pos);//确保节点的有效性
	//如果只有一个节点
	if (pos->_next )//pos节点的下一个节点存在
	{
		SListNode* next = pos->_next;
		SListNode* nextNext = next->_next;
		free(next);//删除节点,重新链接
		pos->_next = nextNext;
	}
}

5.查找链表的值和修改链表节点的值

        遍历链表就可以对链表中的数据进行查找,找到查找的值,就可以对节点的值进行修改。 

SListNode* SListFind(SListNode* pHead, SListDatatype data)//查找
{
	SListNode* cur = pHead;
	while (cur)
	{
		if (cur->_data == data)
			return cur;
		cur = cur->_next;//迭代向后走
	}
	return NULL;//找不到
}

 

void testSList()
{
	//查找和修改的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		//节点的数据
		node->_data = 50;
	}
	SListPrint(pHead);
}

6.销毁链表

void SListDestory(SListNode** ppHead)//销毁
{
	assert(*ppHead);
	//确保指针有效性
	SListNode* cur = *ppHead;
	while (cur)
	{
		SListNode* freeNode = cur;
		cur = cur->_next;
		free(freeNode);

	}
	*ppHead = NULL;
}

 

7.测试代码

void testSListBack()
{
	//尾插尾删的测试代码
	SListNode* pHead = NULL;
	SListPushBack(&pHead, 1);
	SListPushBack(&pHead, 2);
	SListPushBack(&pHead, 3);
	SListPushBack(&pHead, 4);
	SListPushBack(&pHead, 5);
	SListPushBack(&pHead, 6);
	SListPrint(pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);


}
void testSListFront()
{
	//头插头删的测试代码
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
}
void testSList()
{
	//查找和修改的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		//节点的数据
		node->_data = 50;
	}
	SListPrint(pHead);
}
void TestSList1()
{
	//对在pos节点之后进行插入和删除的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		
		//插入节点
		SListInsertAfter(node, -2);
		SListPrint(pHead);

		SListErase(node);
		SListPrint(pHead);

	}
	SListDestory(&pHead);
}

8.全部代码

//SList.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
typedef int SListDatatype;
typedef struct SListNode
{
	SListDatatype _data;//存储节点的数据
	struct SListNode* _next;
}SListNode;
SListNode* SlistBuyNode(SListDatatype data);


void SListDestory(SListNode** ppHead);//销毁
void SListPushBack(SListNode**ppHead,SListDatatype data);//尾插
void SListPopBack(SListNode** ppHead );//尾删

void SListPushFront(SListNode** ppHead, SListDatatype data);//头插
void SListPopFront(SListNode** ppHead);//头删

void SListInsertAfter(SListNode* pos, SListDatatype data);//任意位置的插入

void SListErase(SListNode*pos);//任意位置的删除

SListNode* SListFind(SListNode*pHead, SListDatatype data);//查找
void SListPrint(SListNode* pHead);//显示链表数据
//void SListDestory(SListNode** ppHead);//删除链表

 //SList.c

#include"SList.h"

SListNode* SlistBuyNode(SListDatatype data)
{
	 SListNode*newNode = (SListNode*)malloc(sizeof(SListNode));
	 if (newNode == NULL)
	 {
		 //申请节点失败
		 printf("申请节点失败\n");
		 exit(-1);//暴力返回
	 }
	 newNode->_data = data;//给节点赋值
	 newNode->_next = NULL;

	 return newNode;
}

void SListDestory(SListNode** ppHead)//销毁
{
	assert(*ppHead);
	//确保指针有效性
	SListNode* cur = *ppHead;
	while (cur)
	{
		SListNode* freeNode = cur;
		cur = cur->_next;
		free(freeNode);

	}
	*ppHead = NULL;
}
void SListPushBack(SListNode** ppHead, SListDatatype data)//尾插
{
	SListNode*newNode =  SlistBuyNode(data);//申请一个新的节点
	
	if (*ppHead == NULL)//链表为空
	{
		*ppHead = newNode;
		return;
	}
	if ((*ppHead)->_next == NULL)//链表只存在一个节点
	{
		(*ppHead)->_next = newNode;
		return;
	}
	SListNode* cur = *ppHead;
	while (cur->_next)//找到尾节点
	{
		cur = cur->_next;
	}
	cur->_next = newNode;//进行链接
	return;
}
void SListPopBack(SListNode** ppHead)//尾删
{
	assert(*ppHead);
	if (*ppHead == NULL)//链表为空不需要删除
	{
		return;
	}
	if ((*ppHead)->_next == NULL)
	{
		free(*ppHead);//链表只有一个节点
		(*ppHead) = NULL;
		return;
	}
	SListNode* cur = *ppHead;
	SListNode* prev = NULL;

	while (cur->_next)//找到尾结点
	{
		prev = cur;//保存上一个节点
		cur = cur->_next;
	}
	free(cur);//释放尾结点所在的空间
	prev->_next = NULL;//将上一个节点的_next置空
	return;
}
void SListPushFront(SListNode** ppHead, SListDatatype data)//头插
{
	SListNode* newNode = SlistBuyNode(data);//申请一个新的节点
	if (*ppHead == NULL)
	{
		//链表为空
		*ppHead = newNode;
		return;
	}
	newNode->_next = (*ppHead);
	*ppHead = newNode;//对头结点进行链接
}
void SListPopFront(SListNode** ppHead)//头删
{
	assert(*ppHead);//确保指针的有效性
	if ((*ppHead)->_next == NULL)
	{
		//链表只有一个节点
		free(*ppHead);
		*ppHead = NULL;
		return;
	}
	//删除头结点,然后更新头结点
	SListNode* newHead = (*ppHead)->_next;
	free(*ppHead);
	*ppHead = newHead;
	return;
}
void SListInsertAfter(SListNode*pos, SListDatatype data)//任意位置的插入,在pos之后插入
{
	assert(pos);//确保指针不为空
	SListNode* newNode = SlistBuyNode(data);
	SListNode* next = pos->_next;
	pos->_next = newNode;
	newNode->_next = next;
}
void SListErase(SListNode*pos)//任意位置的删除,pox位置之后的删除
{
	assert(pos);//确保节点的有效性
	//如果只有一个节点
	if (pos->_next )//pos节点的下一个节点存在
	{
		SListNode* next = pos->_next;
		SListNode* nextNext = next->_next;
		free(next);//删除节点,重新链接
		pos->_next = nextNext;
	}
}

SListNode* SListFind(SListNode* pHead, SListDatatype data)//查找
{
	SListNode* cur = pHead;
	while (cur)
	{
		if (cur->_data == data)
			return cur;
		cur = cur->_next;//迭代向后走
	}
	return NULL;//找不到
}
void SListPrint(SListNode* pHead)//显示链表数据
{
	assert(pHead);//确保指针的有效性
	SListNode* cur = pHead;
	while (cur)
	{
		printf("%d ", cur->_data);
		printf("->");
		cur = cur->_next;
	}
	printf("NULL\n");
}

//test.c

#include"SList.h"
void testSListBack()
{
	//尾插尾删的测试代码
	SListNode* pHead = NULL;
	SListPushBack(&pHead, 1);
	SListPushBack(&pHead, 2);
	SListPushBack(&pHead, 3);
	SListPushBack(&pHead, 4);
	SListPushBack(&pHead, 5);
	SListPushBack(&pHead, 6);
	SListPrint(pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);
	SListPopBack(&pHead);


}
void testSListFront()
{
	//头插头删的测试代码
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
	SListPopFront(&pHead);
}
void testSList()
{
	//查找和修改的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		//节点的数据
		node->_data = 50;
	}
	SListPrint(pHead);
}
void TestSList1()
{
	//对在pos节点之后进行插入和删除的测试
	SListNode* pHead = NULL;
	SListPushFront(&pHead, 1);
	SListPushFront(&pHead, 2);
	SListPushFront(&pHead, 3);
	SListPushFront(&pHead, 4);
	SListPushFront(&pHead, 5);
	SListPushFront(&pHead, 6);
	SListPrint(pHead);
	SListNode* node = SListFind(pHead, 5);//查找
	if (node)
	{
		
		//插入节点
		SListInsertAfter(node, -2);
		SListPrint(pHead);

		SListErase(node);
		SListPrint(pHead);

	}
	SListDestory(&pHead);
}
int main()
{
	TestSList1();
	return 0;
}

 

9.总结 

        链表与顺序表区别和联系。顺序表是在数组的基础上实现增删查改的。并且插入时可以动态增长。顺序表的缺陷:可能存在空间的浪费,增容有一定的效率损失,中间或者头部数据的删除,时间复杂度是O(n),因为要挪动数据。这些问题都是由链表来解决的,但是链表也有自己的缺陷,不能随机访问,存在内存碎片等问题。 其实没有哪一种数据结构是完美的,它们都有各自的缺陷,实际中的使用都是相辅相成的。文章来源地址https://www.toymoban.com/news/detail-644026.html

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

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

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

相关文章

  • 【数据结构】单链表的基本操作 (C语言版)

    目录 一、单链表 1、单链表的定义: 2、单链表的优缺点: 二、单链表的基本操作算法(C语言) 1、宏定义 2、创建结构体 3、初始化 4、插入 4、求长度 5、清空 6、销毁  7、取值 8、查找 9、删除 10、头插法创建单链表 11、尾插法创建单链表 三、单链表的全部代码(C语言)

    2024年01月22日
    浏览(37)
  • 【数据结构】 循环单链表的基本操作 (C语言版)

    目录 一、循环单链表 1、循环单链表的定义: 2、循环单链表的优缺点: 二、循环单链表的基本操作算法(C语言)    1、宏定义  2、创建结构体 3、循环单链表的初始化  4、循环单链表的插入 5、求单链表长度 6、循环单链表的清空 7、循环单链表的销毁 8、循环单链表的取

    2024年01月22日
    浏览(41)
  • 【数据结构—单链表的实现】

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 目录 前言 1. 链表的概念及结构 2. 单链表的实现 2.1单链表头文件——功能函数的定义 2.2单链表源文件——功能函数的实现 2.3 单链表源文件——功能的测试 3.具体的理解操作图 4. 链表的分类 总结 世上

    2024年02月05日
    浏览(44)
  • 【数据结构】-- 单链表的实现

    在前面我们学习了顺序表,顺序表在数组的基础上提供了很多现成的方法,方便了我们对数据的管理,但是我们也发现顺序表有着许多不足: 在处理大型的数据时,需要频繁的增容且在中间删除或插入数据时需要遍历顺序表,这些性质导致了顺序表的 效率较低 。这时我们就

    2024年04月27日
    浏览(30)
  • 【(数据结构)— 单链表的实现】

    概念: 链表是⼀种 物理存储结构上非连续、 非顺序的存储结构,数据元素的 逻辑顺序 是通过链表中的 指针链接 次序实现的 。 链表的结构跟⽕⻋⻋厢相似,淡季时⻋次的⻋厢会相应减少,旺季时⻋次的⻋厢会额外增加⼏节。只需要将⽕⻋⾥的某节⻋厢去掉/加上,不会影响

    2024年02月08日
    浏览(32)
  • 【数据结构】单链表的实现

    🌇个人主页:平凡的小苏 📚学习格言:别人可以拷贝我的模式,但不能拷贝我不断往前的激情 🛸C语言专栏:https://blog.csdn.net/vhhhbb/category_12174730.html 🚀数据结构专栏:https://blog.csdn.net/vhhhbb/category_12211053.html         家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我

    2023年04月09日
    浏览(36)
  • C语言简单的数据结构:单链表的有关算法题(2)

    接着我们介绍后面的三道题,虽然代码变多了但我们的思路更加通顺了 题目链接:https://leetcode.cn/problems/merge-two-sorted-lists/ 创建新链表,遍历原链表,将节点值小的进行尾插到新链表中 这里要多次进行对NULL的判断,开始传入列表,中间newHead的判断,循环出来一个为NULL的判断

    2024年04月15日
    浏览(42)
  • 【数据结构】单链表的层层实现!! !

    关注小庄 顿顿解馋(●’◡’●) 上篇回顾 我们上篇学习了本质为数组的数据结构—顺序表,顺序表支持下标随机访问而且高速缓存命中率高,然而可能造成空间的浪费,同时增加数据时多次移动会造成效率低下,那有什么解决之法呢?这就得引入我们链表这种数据结构 概念

    2024年03月12日
    浏览(34)
  • 【数据结构】单链表的简单实现

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元

    2024年02月04日
    浏览(41)
  • 【数据结构】实现单链表的增删查

    链表类和节点类的定义: 图解: 从中间位置插入: 图解:假定index=2 尾插: 删除当前线性表中索引为index的元素,返回删除的元素值: 图解: 删除当前线性表中第一个值为element的元素: 删除当前线性表中所有值为element的元素: 将当前线性表中index位置的元素替换为eleme

    2024年02月14日
    浏览(39)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包