C语言实现单链表的基本操作分享

 更新时间:2022年10月23日 14:53:48   作者:从未止步..  
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。本文将为大家介绍C语言中单链表的基本操作,需要的可以参考一下

导语

无论是顺序存储结构还是链式存储结构,在内存中进行存放元素的时候,不仅需要存放该元素的相关信息,还需要存放该元素和其他元素之间的关系,而我们之前所学的顺序表“与生俱来”的物理结构自然地能够表达出元素和元素之间的关系,不需要额外的信息去表达元素和元素之间的关系,而对于链式存储这种非顺序存储的结构,需要额外附加指针去表示这种关系。

单链表

每个结点除了存放数据元素外,还要存储指向下一个节点的指针。

单链表的特点

优点:不要求大片连续空间,改变容量方便

缺点:不可随机存取,要耗费一定空间存放指针

定义

typedef struct LNode {
	int data;
	struct LNode* next;//指针指向下一个节点,指针的类型为节点类型;
}*LinkNode;//声明*LinkNode为结构体指针类型

除了上述这种方法外,我们还可以先先声明LinkNode为结构体类型,在使用该类型的时候,将对应的变量定义为指针即可。

单链表分为带头结点和不带头结点,我们一般主要学习带头结点的。

初始化操作

在所有的操作之前,我们首先需要建立一个空的单链表,那么首先需要做的就是分配头结点。

void InistLinkNode(LinkNode& L) {
	L = (LNode*)malloc(sizeof(LNode));//分配头结点
	L->next = NULL;
}

头插法

“头插法”顾名思义就是将元素插入到头结点之后,插入一次好像和我们通常所讲的插入没什么区别,但多次这样插到头结点之后,也就是“第一个真正的节点”,那么是不是会产生一种现象,它最终的存储数据和我们所插入时的顺序是相反的。

void InsertLinkNode(LinkNode& L) {
	LNode* s;
	int x,Length;
	printf("请输入你要插入的元素个数:");
	scanf("%d", &Length);
	printf("请输入你要插入的元素:\n");
	for (int j = 0; j < Length; j++) {
		s = (LNode*)malloc(sizeof(LNode));//每插入一个元素之前,都需要给它分配节点空间
		scanf("%d", &x);
		s->data = x;
		s->next = L->next;
		L->next = s;
	}

} 

通过程序验证以下:

尾插法

“尾插法”顾名思义就是将元素插入到表尾,也就是我们普通的插入,那么怎么要找到表尾的位置呢?在顺序表中,我们完全可以利用它顺序存储结构的天然特性,通过下标即可以找到,但是单链表是没有办法的,我们只有两种方式,要么循环遍历,要么尝试在表尾的地方做个标记。

那么那种方法是好的呢?

答案是第二种!循环遍历的方式,如果只插入一个元素看似没什么问题,但如果多次的重复遍历循环无疑增加了时间复杂度,这显然不是好的方法。

第二个方法就不存在时间复杂度的问题,只需要在表尾位置做个标记,使它永远指向表尾即可。

void TailInsertLinkNode(LinkNode& L) {
	LNode* s,*r;
	int x,Length;
	r = L;//r为表尾指针
	printf("请输入你要插入的元素个数:");
	scanf("%d", &Length);
	printf("请输入你要插入的元素:\n");
	for (int j = 0; j < Length; j++) {
		s = (LNode*)malloc(sizeof(LNode));
		scanf("%d", &x);
		s->data = x;
		r->next = s;
		r = s;//s为当前的表尾指针,将他的值赋值给r----使r永远指向表尾
	}
	printf("\n");
	r->next = NULL;
}

删除第i个元素

既然要删除某个元素,那么首先我们需要保证这个元素是非NULL,其次,我们还需要保证它前面的那个节点也是非NULL,为什么呢?因为如果将该元素从链表中删除后,只有前面节点非NULL的情况下,才可以实现后续元素和前面子表的连接。

void DeleteLinkNode(LinkNode& L) {
	int x, j = 0,e;
	printf("请输入你要删除的元素位序:\n");
	scanf("%d", &x);
	LNode*p = L;
	while (p != NULL && j < x - 1) {//寻找要删除元素前的元素
		p = p->next;
		j++;
	}
	if (p == NULL)
	{
		printf("不存在我们要删除的元素!");
	}
	if (p->next == NULL)//判断该要删除的节点是否为NULL
	{
		printf("不存在我们要删除的元素!");
	}
	LNode* q = p->next;//q为我们要删除的节点
	e = q->data;
	p->next = q->next;
	free(q);//需要及时的将删除了的元素空间进行释放
}

其他的基本操作都是很常规化的,这里就不单独的进行解释了,需要注意的点,我会在文章结尾部分的完整代码的注释中展出。

在第i个位置插入

void IncreaseLinkNode(LinkNode& L) {
	printf("请输入你要插入的元素和位序:(元素和位序之间用逗号隔开)\n");
	int x, j = 0, e;
	scanf("%d,%d",&e, &x);
	LNode* s = L, * r= (LNode*)malloc(sizeof(LNode));
	while (j < x-1  && s != NULL) {
		j++;
		s = s->next;
	}
	r->data = e;
	r->next = s->next;
	s->next = r;
}

如下所示的代码顺序不能发生改变,否则会出现无法和后面的节点;

r->next = s->next;
s->next = r;

完整代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode {
	int data;
	struct LNode* next;
}*LinkNode;
//初始化
void InistLinkNode(LinkNode& L) {
	L = (LNode*)malloc(sizeof(LNode));//分配头结点
	L->next = NULL;
}
//头插法
void InsertLinkNode(LinkNode& L) {
	LNode* s;
	int x,Length;
	printf("请输入你要插入的元素个数:");
	scanf("%d", &Length);
	printf("请输入你要插入的元素:\n");
	for (int j = 0; j < Length; j++) {
		s = (LNode*)malloc(sizeof(LNode));
		scanf("%d", &x);
		s->data = x;
		s->next = L->next;
		L->next = s;
	}
} 
//尾插法
void TailInsertLinkNode(LinkNode& L) {
	LNode* s,*r;
	int x,Length;
	r = L;
	printf("请输入你要插入的元素个数:");
	scanf("%d", &Length);
	printf("请输入你要插入的元素:\n");
	for (int j = 0; j < Length; j++) {
		s = (LNode*)malloc(sizeof(LNode));
		scanf("%d", &x);
		s->data = x;
		r->next = s;
		r = s;
	}
	printf("\n");
	r->next = NULL;
}
//输出单链表
void PrintLinkNode(LinkNode& L)
{
	LNode* s=L->next;
	printf("单链表元素如下:\n");
	while (s != NULL) {
		printf("%d", s->data);
		s =s->next;
	}
	printf("\n");
}
//求线性表长度
void lengthLinkNode(LinkNode& L)
{
	LNode* s = L->next;
	int n=0;
	while (s != NULL) {
		n++;
		s = s->next;
	}
	printf("单链表长度为:%d",n);
	printf("\n");
}
//取第i个元素
void GetElemLinkNode(LinkNode& L) {
	printf("请输入你要查找的元素位序:\n");
	int i, j = 0;
	LNode* s=L;
	scanf("%d", &i);
	while (j < i && s != NULL) {
		j++;
		s = s->next;
	}
	if (s == NULL) {
		printf("不存在我们要查找的元素!");
	}
	else {
		printf("元素位序为%d的元素是%d",i, s->data);
	}
	printf("\n");
}
//删除第i个元素
void DeleteLinkNode(LinkNode& L) {
	int x, j = 0,e;
	printf("请输入你要删除的元素位序:\n");
	scanf("%d", &x);
	LNode*p = L;
	while (p != NULL && j < x - 1) {
		p = p->next;
		j++;
	}
	if (p == NULL)
	{
		printf("不存在我们要删除的元素!");
	}
	if (p->next == NULL)
	{
		printf("不存在我们要删除的元素!");
	}
	LNode* q = p->next;
	e = q->data;
	p->next = q->next;
	free(q);
}
//在第i个位置插入
void IncreaseLinkNode(LinkNode& L) {
	printf("请输入你要插入的元素和位序:(元素和位序之间用逗号隔开)\n");
	int x, j = 0, e;
	scanf("%d,%d",&e, &x);
	LNode* s = L, * r= (LNode*)malloc(sizeof(LNode));
	while (j < x-1  && s != NULL) {
		j++;
		s = s->next;
	}
	r->data = e;
	r->next = s->next;
	s->next = r;
}
//查找位序
void SearchLinkNode(LinkNode &L) {
	int x,j=1;
	LNode* p=L->next;
	printf("请输入你要查找的元素:\n");
	scanf("%d", &x);
	while (p != NULL && p->data != x) {
		p = p->next;
		j++;
	}
	if (p == NULL) {
		printf("您要查找的元素不存在!");
	}
	else {
		printf("你要查找的元素%d的位序为%d", x, j);
	}
}
int main() {
	LinkNode L;
	InistLinkNode(L);
	/*InsertLinkNode(L);*/
	TailInsertLinkNode(L);
	PrintLinkNode(L);
	lengthLinkNode(L);
	GetElemLinkNode(L);
	IncreaseLinkNode(L);
	PrintLinkNode(L);
	DeleteLinkNode(L);
	PrintLinkNode( L);
	SearchLinkNode(L);
}

输出:

到此这篇关于C语言实现单链表的基本操作分享的文章就介绍到这了,更多相关C语言单链表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言实现斐波那契数列(非递归)的实例讲解

    C语言实现斐波那契数列(非递归)的实例讲解

    下面小编就为大家带来一篇C语言实现斐波那契数列(非递归)的实例讲解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-08-08
  • C++中默认无参构造函数的工作机制浅析

    C++中默认无参构造函数的工作机制浅析

    构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用;析构函数主要作用在于对象销毁前系统自动调用,执行一些清理工作
    2023-02-02
  • C++ STL中五个常用算法使用教程及实例讲解

    C++ STL中五个常用算法使用教程及实例讲解

    本文主要介绍了C++ STL算法中常见的五个算法的使用教程并附上了案例详解,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-11-11
  • C语言动态内存规划详解

    C语言动态内存规划详解

    这篇文章主要介绍了C语言动态内存的规划,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-10-10
  • C++类的构造与析构特点及作用详解

    C++类的构造与析构特点及作用详解

    本文章将会可能会涉及到汇编的知识,不过没有关系,我会讲的尽量通俗易懂;另外本篇文章开始前,建议了解下什么是函数重载,这个概念很简单的--有相同的函数名,但参数列表不相同的函数,就是函数重载
    2022-10-10
  • 图文详解C语言位运算基础知识

    图文详解C语言位运算基础知识

    这篇文章主要以图文结合的方式为大家详细介绍了C语言位运算基础知识,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • C++标准模板库STL深入讲解

    C++标准模板库STL深入讲解

    STL提供了一组表示容器、迭代器、函数对象和算法的模板。容器是一个与数组类似的单元,可以存储若干个值。STL容器是同质的,即存储的值的类型相同:算法是完成特定任务(如对数组进行排序或在链表中查找特定值)的处方
    2022-12-12
  • C语言实现弹跳小球

    C语言实现弹跳小球

    这篇文章主要为大家详细介绍了C语言实现弹跳小球,设置了小球分数及过关难度,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-03-03
  • Qt写入Json文件的方法详解(含源码+注释)

    Qt写入Json文件的方法详解(含源码+注释)

    在Qt库中,为JSON的相关操作提供了完整的类支持,下面这篇文章主要给大家介绍了关于Qt写入Json文件(含源码+注释)的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-10-10
  • C语言实现设备管理系统

    C语言实现设备管理系统

    这篇文章主要为大家详细介绍了C语言实现设备管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06

最新评论