单链概念

链表中的数据是以结点表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据存储单元指针就是连接每个结点地址数据。以“结点序列表示线性表称作线性链表单链表),单链表是链式存取的结构

链接存储方法

链接方式存储的线性表称为链表(Linked List)。 链表的具体存储表示为: ①
一组任意的存储单元存放线性表结点(这组存储单元可以是连续的,也可以是不连续的) ②
链表结点逻辑次序和物理次序不一定相同。为了能正确表示结点间的逻辑关系,在存储每个结点值的同时,还必须存储指示其后继结点地址(或位置)信息称为指针(pointer)或链(link))
链式存储是最常用的存储方式之一,它不仅可用来表示线性表,而且可用表示各种非线性数据结构
结点结构
在这里插入图片描述

data域–存放结点值的数据next域–存放结点的直接后继的地址(位置)的指针域(链域)
链表通过每个结点的链域将线性表的n个结点按其逻辑顺序链接在一起的,每个结点只有一个链域的链表称为单链表(Single Linked
List)。

头指针head终端结点

链表每个结点的存储地址是存放在其前趋结点next域中,而开始结点无前趋,故应设头指针head指向开始结点。链表由头指针唯一确定,单链表可以用头指针的名字命名
终端结点无后继,故终端结点的指针域为空,即NULL。

链接过程

逻辑上:
在这里插入图片描述
从物理上:
在这里插入图片描述

单链表的优缺点

1、优点:

插入删除操作方便,在单链表中插入删除节点时,只需修改相邻节点游标即可,不需要移动大量数据,因此操作效率较高。适合动态存储,单链表可以随时插入删除节点,因此适合动态存储数据空间利用率高,单链表不需要连续的存储空间,因此可以更有效利用内存空间

2、缺点:

查找效率低,在单链表中,查找某个元素需要从头节点开始遍历整个链表,因此查找效率较低。
需要额外空间存储游标,单链表需要额外的空间存储游标,这会增加内存空间的消耗。实现复杂度较高,相比数组等数据结构,单链表的实现复杂度较高,需要维护节点引用关系

实现

一.思路
1.利用结构体来储存数据和指针(结构能够存储不同类型数据)
2.每增加一个数据就通过malloc函数扩展一个空间
3.通过多文件的方式来实现
4.我们实现打印扩容、尾插、头插、尾删、头删接口函数
二.框架
结构创建、一些定义

#include<stdio.h>
#include<stdlib.h&gt;//动态内存函数头文件
typedef int SLTDataType;//类型定义
struct SListNode
{
	SLTDataType data;//数据
	struct SListNode* next;//链接
};
typedef struct SListNode SLTNode;//类型定义

函数

void  Test() {
	SLTNode* plist = NULL;//头指针初始化  因为开始是没有数据
	}
int main() {
	Test();//调用函数
	return 0;
}

三.接口函数的实现
1.打印函数
(1)参数:结构体指针类型接收头指针),由于不需要改变头指针,所以传一个头指针变量过来就行
(2)迭代:将后一个链接的指针变量 next 传给指针phead找到一个结点
代码实现:

void SListPrint(SLTNode* phead) {//打印函数
	//由于不需要改变头指针,所以传一个头指针变量过来就行了
	while (phead != NULL) {//将phead不是空指针之前的数据打印
		printf("%d-&gt;", phead-&gt;data);//打印数据
		phead = phead-&gt;next;//迭代
	}
	printf("NULL");//最后打印指向的NUll
}

2.扩容函数:
(1)我们通过malloc函数来实现扩容
(2)参数:插入的数据
(3)返回类型返回扩建的空间指针变量
(4)过程:在扩容后将插入的数据存储到这个空间里。并把指针变量置空
代码实现:

SLTNode* uuu(SLTDataType x) {//扩容和储存数据
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode-&gt;data = x;//储存数据
	newnode-&gt;next = NULL;//将扩容和的链接点置空
	return newnode;//放回这个空间的地址
}

3.尾插函数:
有两种情况:
(1)头指针为空,此时将扩建的第一个空间直接当头指针
在这里插入图片描述
(2)头指针不为空,此时将我们要通过头指针找到最后一个结点,再用这个结点来链接我们扩建的空间
在这里插入图片描述
注意
1.在实现这个过程可能会改变头指针,所以传参需要使用传址调用

2.第二种情况不要改变头指针

代码实现:

void SListPushBack(SLTNode** pphead, SLTDataType x) {//尾插
//注意这里需要传参需要进行传址调用
	SLTNode* newnode = uuu(x);//插入一个数据需要再创建一个新的空间
	if (*pphead == NULL)//判断是否是第一种情况
		*pphead = newnode;
	else
	{
		SLTNode* cat = *pphead;//防止头指针被改变
			while(cat-&gt;next!=NULL){//找到最后一个结点
				cat = cat->next;//迭代
		}
			cat->next = newnode;//将扩展的空间连接最后一个结点的next上
	}
}

检查
我们尾插 1、2

SListPushBack(&amp;plist, 1);
SListPushBack(&amp;plist, 2);
SListPrint(plist);

在这里插入图片描述

4.头插函数:
(1)我们直接创建的空间的next与第一个结点连接即可
在这里插入图片描述
注意:在实现这个过程会改变头指针,所以传参需要使用传址调用
代码实现:

void SListPushFront(SLTNode** pphead, SLTDataType x) {头插
	SLTNode* newnode = uuu(x);//扩建空间
		newnode->next = *pphead;//与第一个结点连接即头指针
		*pphead = newnode;//将头指针改为头插的空间
}

检查
我们头插一个 0

void  Test() {
	SLTNode* plist = NULL;
	SListPushBack(&amp;plist, 1);
	SListPushBack(&amp;plist, 2);
	SListPushFront(&amp;plist, 0);
		SListPrint(plist);
}

运行结果
在这里插入图片描述
5,头删函数:
(1)这个我们不用创建新的空间但是要释放空间。释放函数 ferr()
(2)我们可以创建一个新的指针变量来接收第一个结点的next(第二个结点的地址),然后将第一个结点释放掉,再然后将新的指针变量当作头指针
(3)在实现这个过程会改变头指针,所以传参需要使用传址调用
代码实现:

void SListPopFront(SLTNode** pphead) {//头删
	SLTNode* next = (*pphead)->next;//创建一个新的指针变量来接收第一个结点的next(第二个结点的地址)
	free(*pphead);//释放
	*pphead = next;//重新设置头指针
}

检查
头删一个数据:

void  Test() {
	SLTNode* plist = NULL;
	SListPushBack(&amp;plist, 1);
	SListPushBack(&amp;plist, 2);
	SListPushFront(&amp;plist, 0);
	SListPopFront(&amp;plist);
			SListPrint(plist);
}

运行结果
在这里插入图片描述
6.尾删函数:
三种情况:
(1)没有结点
返回NULL
(2)有一个结点
将其直接释放,并置空
(3)多个结点
我们要创建两个新的变量分别来找倒数第一和第二个结点,释放最后一个结点倒数,第二个结点next置空

代码实现:

void SListPopBack(SLTNode** pphead) {//尾删
	if (*pphead == NULL)//当为空是直接返回
		return;
	else if ((*pphead)->next == NULL) {//只有一个结点时,直接释放掉pphead并将其置空
		free(*pphead);
		*pphead = NULL;
	}
	else {//多个结点
		SLTNode* cat = *pphead;//为了不改变头指针,创建一个新的变量
	    SLTNode* cat1= NULL;//用于找到倒数第二个结点,最后将其next置空
		while (cat->next != NULL) {//找最后一个结点
			cat1 = cat;
			cat = cat->next;
		}
		free(cat);//释放最后一个结点
		cat1->next = NULL;//倒数第二个结点next置空
	}
}

检查
尾删一个数

void  Test() {
	SLTNode* plist = NULL;
	SListPushBack(&amp;plist, 1);
	SListPushBack(&amp;plist, 2);
	SListPushFront(&amp;plist, 0);
	SListPopFront(&amp;plist);
	SListPopBack(&amp;plist);		
	SListPrint(plist);
}

运行结果
在这里插入图片描述

代码一览

SList.h:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>//动态内存函数的头文件
typedef int SLTDataType;//类型定义
struct SListNode
{
	SLTDataType data;//数据
	struct SListNode* next;//链接点
};
typedef struct SListNode SLTNode;//类型定义

// 不会改变链表的头指针,传一级指针
void SListPrint(SLTNode* phead);

// 可能会改变链表的头指针,传二级指针
void SListPushBack(SLTNode** pphead, SLTDataType x);//尾插
void SListPushFront(SLTNode** pphead, SLTDataType x);//头插
void SListPopFront(SLTNode** pphead);头删
void SListPopBack(SLTNode** pphead);//尾删

SList.c

#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"

void SListPrint(SLTNode* phead) {//打印函数
	//由于不需要改变头指针,所以传一个头指针变量过来就行了
	while (phead != NULL) {//将phead不是空指针之前的数据打印
		printf("%d->", phead->data);//打印数据
		phead = phead->next;//迭代
	}
	printf("NULL");//最后打印指向的NUll
}


SLTNode* uuu(SLTDataType x) {//扩容和储存数据
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	newnode->data = x;//储存数据
	newnode->next = NULL;//将扩容和的链接点置空
	return newnode;//放回这个空间的地址
}

void SListPushBack(SLTNode** pphead, SLTDataType x) {//尾插
	SLTNode* newnode = uuu(x);
	if (*pphead == NULL)
		*pphead = newnode;
	else
	{
		SLTNode* cat = *pphead;
			while(cat->next!=NULL){
				cat = cat->next;
		}
			cat->next = newnode;
	}
}
void SListPushFront(SLTNode** pphead, SLTDataType x) {头插
	SLTNode* newnode = uuu(x);
		newnode->next = *pphead;
		*pphead = newnode;
}
void SListPopFront(SLTNode** pphead) {//头删
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

void SListPopBack(SLTNode** pphead) {//尾删
	if (*pphead == NULL)
		return;
	else if ((*pphead)->next == NULL) {
		free(*pphead);
		*pphead = NULL;
	}
	else {
		SLTNode* cat = *pphead;
	    SLTNode* cat1= NULL;
		while (cat->next != NULL) {
			cat1 = cat;
			cat = cat->next;
		}
		free(cat);
		cat1->next = NULL;
	}
}

Test:

#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"


void  Test() {
	SLTNode* plist = NULL;
	SListPushBack(&amp;plist, 1);
	SListPushBack(&amp;plist, 2);
	SListPushFront(&amp;plist, 0);
	SListPopFront(&amp;plist);
	SListPopBack(&amp;plist);
	
		SListPrint(plist);
}
int main() {
	Test();
	return 0;
}

还有查改等没写,有兴趣的可以去试试哦
以上就是我的分享了,如果有什么错误,欢迎在评论留言
最后,谢谢大家的观看!

原文地址:https://blog.csdn.net/2302_79539362/article/details/134675754

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_46702.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注