链表(C语言)

前言:前面几篇文章我们详细介绍了顺序表,以及基于顺序表来实现的通讯录。今天我们连介绍一下链表的下一个结构链表。那么链表和顺序表究竟有什么区别呢?他们两个的优缺点分别是什么。今天这篇文章就带大家了解一下链表。

目录

一.链表的概念

二.链表的分类 

三.链表的实现 

1.尾插链表

 2.尾删链表

3.头插链表 

4.头删链表

5.查找节点

6.打印链表

 7.指定位置后插入链表

8.删除指定位置链表

三.链表所有代码

四.结言


一.链表的概念

概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

首先我们先构造一个链表的基本结构:

typedef int ListType;
typedef struct List
{
	ListType x;
	struct List* next;
}List;

这里我们x存放我们需要储存的数据,next指针指向下一个数据节点的地址。

大概就是这样一个形态。

 相当于一个火车的模型一节连着一节,这样就不用担心找不到下一节车厢了。

注意:

1.链式结构在逻辑上来讲是连续的,但是在物理结构上不一定连续。

2.现时结构上的内存都是从堆上申请过来的。

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

二.链表的分类 

从结构上来讲链表分为:

1.单向或双向

2. 带头或不带头

3.循环或非循环

但是我们实际应用中最常用到的还是:

无头单向非循环链表

有头双向循环链表

 

1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结
构的子结构,如哈希桶、图的邻接表等等。

2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。

三.链表的实现 

1.尾插链表

这里由于许多函数都要用到创建内容,所以我们把创建空间分装成一个函数以便后续的操作。

List* BuyMemory(List* ls, ListType x)
{
	List* tem = (List*)malloc(sizeof(List));
	if (tem == NULL)
	{
		perror(malloc);
		return;
	}
	tem->val = x;
	tem->next = NULL;
	return tem;
}

接下来是尾插的内容:

void ListBackPush(List** ls, ListType x)
{
	List*newnext=BuyMemory(*ls,x);
	if (*ls == NULL)
	{
		*ls = newnext;
		(*ls)->next = NULL;
	}
	else
	{
		List* pcur = *ls;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		pcur->next = newnext;
	}
}}

 2.尾删链表

void ListBackPop(List** ls)
{
	assert(*ls);
	if ((*ls)->next == NULL)
	{
		*ls = NULL;
		return;
	}
	List* pcur = *ls;
	while (pcur->next->next)
	{
		pcur = pcur->next;
	}
	pcur->next = NULL;
	free(pcur->next);
	
}

尾删我们只需要找到下一个节点为空的节点然后将他free掉就可以实现我们的尾删操作。

3.头插链表 

void ListFrontPush(List** ls, ListType x)
{
	List* newnext = BuyMemory(*ls, x);

		newnext->next = (*ls);
		(*ls) = newnext;
	
}

这里我们需要注意的是 newnext->next = (*ls)   (*ls) = newnext这两句代码的位置不能改变否则就找不到(*ls)的下一个节点。

4.头删链表

void ListFrontPop(List** ls)
{
	assert(*ls);
	List* pcur = *ls;
	(*ls) = pcur->next;
	free(pcur);
	pcur = NULL;
}

与头插链表一样需要注意的是链表之间的前后关系,插入之前要弄清楚每个节点的后节点于前节点有什么改变即可。

5.查找节点

List* ListFind(List** ls, ListType x)
{
	assert(*ls);
	List* pcur = *ls;
	int count = 1;
	while (pcur->val != x)
	{
		pcur = pcur->next;
		count++;
	}
	if (pcur)
	{
		prinft("找到了\n");
		return pcur;
	}
	else
	{
		printf("找不到\n");
		return 0;
	}
}

遍历链表查找所寻找节点的val,找到后返回该节点的地址。 如果找不到则返回0。

6.打印链表

void ListPrint(List** ls)
{
	assert(*ls);
	List* pcur = *ls;
	while (pcur)
	{
		printf("%d->", pcur->val);
		pcur = pcur->next;
	}
}

我们所需要做的就是遍历链表并打印。

 7.指定位置后插入链表

void ListRandomPush(List** ls, ListType x, List* flag)
{
	assert(ls);
	assert(*ls);
	List* newnext = BuyMemory(*ls, x);
	List* pcur = *ls;
	while (pcur != flag)
	{
		pcur = pcur->next;
	}
	newnext->next = pcur->next;
	pcur->next = newnext;
}

同样需要注意的是每个节点之间的关系,要注意 newnext->next = pcur->next; pcur->next = newnext位置不可以改变否则会找不到pcur的next。

8.删除指定位置链表

void ListRandomPop(List** ls, List* flag)
{
	assert(ls);
	assert(*ls);
	List* pcur = *ls;

	while (pcur->next != flag)
	{
		pcur = pcur->next;
	}
	List* prev = pcur->next;
	pcur->next = pcur->next->next;
	free(prev);
	prev = NULL;
}

我们需要注意各个节点之间的关系,处理好他们的链接关系就可以了。

还有许多可以实现的操作但实现方法大都大同小异,以上的代码就可以解决大部分的实际问题,大家感兴趣的可以自己去写一下其他的操作。

三.链表所有代码

List.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

typedef int ListType;
typedef struct List
{
	ListType val;
	struct List* next;
}List;

void ListBackPush(List** ls,ListType x);
void ListBackPop(List** ls);
void ListFrontPush(List** ls, ListType x);
void ListFrontPop(List** ls);
List* ListFind(List** ls, ListType x);
void ListRandomPush(List** ls, ListType x,List*flag);
void ListRandomPop(List** ls, List* flag);
void ListPrint(List** ls);

List.c
nclude"List.h"


List* BuyMemory(List* ls, ListType x)
{
	List* tem = (List*)malloc(sizeof(List));
	if (tem == NULL)
	{
		perror(malloc);
		return;
	}
	tem->val = x;
	tem->next = NULL;
	return tem;
}
void ListBackPush(List** ls, ListType x)
{
	List*newnext=BuyMemory(*ls,x);
	if (*ls == NULL)
	{
		*ls = newnext;
		(*ls)->next = NULL;
	}
	else
	{
		List* pcur = *ls;
		while (pcur->next)
		{
			pcur = pcur->next;
		}
		pcur->next = newnext;
	}
}
void ListBackPop(List** ls)
{
	assert(*ls);
	if ((*ls)->next == NULL)
	{
		*ls = NULL;
		return;
	}
	List* pcur = *ls;
	while (pcur->next->next)
	{
		pcur = pcur->next;
	}
	pcur->next = NULL;
	free(pcur->next);
	
}
void ListFrontPush(List** ls, ListType x)
{
	List* newnext = BuyMemory(*ls, x);

		newnext->next = (*ls);
		(*ls) = newnext;
	
}
void ListFrontPop(List** ls)
{
	assert(*ls);
	List* pcur = *ls;
	(*ls) = pcur->next;
	free(pcur);
	pcur = NULL;
}
List* ListFind(List** ls, ListType x)
{
	assert(*ls);
	List* pcur = *ls;
	int count = 1;
	while (pcur->val != x)
	{
		pcur = pcur->next;
		count++;
	}
	if (pcur)
	{
		printf("找到了\n");
		return pcur;
	}
	else
	{
		printf("找不到\n");
		return 0;
	}
}
void ListPrint(List** ls)
{
	assert(*ls);
	List* pcur = *ls;
	while (pcur)
	{
		printf("%d->", pcur->val);
		pcur = pcur->next;
	}
}
void ListRandomPush(List** ls, ListType x, List* flag)
{
	assert(ls);
	assert(*ls);
	List* newnext = BuyMemory(*ls, x);
	List* pcur = *ls;
	while (pcur != flag)
	{
		pcur = pcur->next;
	}
	newnext->next = pcur->next;
	pcur->next = newnext;
}
void ListRandomPop(List** ls, List* flag)
{
	assert(ls);
	assert(*ls);
	List* pcur = *ls;

	while (pcur->next != flag)
	{
		pcur = pcur->next;
	}
	List* prev = pcur->next;
	pcur->next = pcur->next->next;
	free(prev);
	prev = NULL;
}

text.c 

#include"List.h"
void test01()
{
	List* ls = NULL;

	ListBackPush(&ls, 1);
	ListBackPop(&ls);
	ListFrontPush(&ls, 1);
	ListBackPush(&ls, 2);
	ListBackPush(&ls, 3);
	ListBackPush(&ls, 4);
	List* ret = ListFind(&ls, 2);
	ListRandomPush(&ls, 100, ret);
	List* ret1 = ListFind(&ls, 100);
	ListRandomPop(&ls, ret1);
	ListPrint(&ls);
	//ListFrontPop(&ls);

}
int main()
{
	test01();
	return 0;
}

四.结言

以上就是链表操作的所有内容了,我们实现的只是无头单向非循环链表,但是其他链表的实现操作也是万变不离其中的只需要处理好各个节点之间的关系就问题就迎刃而解了。

还有请喜欢的朋友们一键三连哦!

谢谢大家了!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/555338.html

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

相关文章

前端三大件速成 01 HTML

文章目录 一、前端基础知识二、标签1、什么是标签2、标签的属性3、常用标签&#xff08;1&#xff09;声明&#xff08;2&#xff09;注释&#xff08;3&#xff09;html 根标签&#xff08;3&#xff09;head标签&#xff08;4&#xff09;body标签 三、特殊字符四、其他标签1…

202462读书笔记|《一世珍藏的诗歌200首》——你曾经羞赧地向我问起, 是谁最早在此留下足印

202462读书笔记|《一世珍藏的诗歌200首》——你曾经羞赧地向我问起&#xff0c; 是谁最早在此留下足印 《一世珍藏的诗歌200首》作者金宏宇&#xff0c;很多美好的诗&#xff0c;有徐志摩&#xff0c;戴望舒&#xff0c;林徽因&#xff0c;舒婷等的诗精选&#xff0c;很值得一读…

变配电场所智能综合监控系统无人化与自动化升级改造

一 项目背景 国家电力建设飞速发展,为了提高管理水平,智能化建设迫在眉睫。变配电场所作为电网中的核心单元,数量巨大,是智能化建设的中坚部分。但由于变配电场所分布的地理位置过于分散&#xff0c;且配电网的自动化水平有待提高,单纯依靠人力来对变配电场所进行巡视,不仅增加…

Leo赠书活动-24期 【三大层次学习企业架构框架TOGAF】文末送书

✅作者简介&#xff1a;大家好&#xff0c;我是Leo&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Leo的博客 &#x1f49e;当前专栏&#xff1a; 赠书活动专栏 ✨特色专栏&#xff1a;…

【网站项目】自习室预约系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

基于Springboot+Vue的Java项目-企业客户管理系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

【Python】OPC UA模拟服务器实现

目录 服务器模拟1. 环境准备2. 服务器设置3. 服务器初始化4. 节点操作5. 读取CSV文件6. 运行服务器 查看服务器客户端总结 在工业自动化和物联网&#xff08;IoT&#xff09;领域&#xff0c;OPC UA&#xff08;开放平台通信统一架构&#xff09;已经成为一种广泛采用的数据交换…

如何使用Docker部署WPS Office服务并实现无公网IP远程处理文档表格

文章目录 1. 拉取WPS Office镜像2. 运行WPS Office镜像容器3. 本地访问WPS Office4. 群晖安装Cpolar5. 配置WPS Office远程地址6. 远程访问WPS Office小结 7. 固定公网地址 wps-office是一个在Linux服务器上部署WPS Office的镜像。它基于WPS Office的Linux版本&#xff0c;通过…

【深入解析spring cloud gateway】13 Reactive Feign的使用

问题引入 在gateway中如果使用feignClient的话&#xff0c;会报如下错误 java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http-nio-3at reactor.core.publisher.BlockingSingleSubscriber.bloc…

合并区间详解

题目&#xff1a; 大体思路&#xff1a; 先排序&#xff0c;设置一个新数组&#xff0c;将原数组遍历的第一位添加到新数组&#xff0c;之后不断的将遍历原数组后的起始位置和新数组的终止位置进行比较&#xff0c;大于&#xff0c;则添加到新数组&#xff0c;不大于&#xff…

OpenHarmony轻量系统开发【13】鸿蒙小车开发

13.1 小车介绍 基于鸿蒙系统 Hi3861 的WiFi小车 首先&#xff0c;我们得有一套WiFi小车套件&#xff0c;其实也是Hi3861 加上电机、循迹模块、超声波等模块。 小车安装完大概是这样&#xff1a; 13.2 电机驱动 我们这里先只做最简单的&#xff0c;驱动小车的电机&#xff…

Simlab python二次开发1-将所有缸套内表面半径加大1mm

Simlab python二次开发1-将所有缸套内表面半径加大1mm 1、打开模型文件2、getBodiesWithSubString&#xff08;&#xff09;从名字得到Bodies3、建Body类Group3.1、定义放入Group中的Bodies3.2、建Group 4、将缸套内表面建组&#xff0c;并扩半径1mm4.1、simlab.getBodiesFromG…

【嵌入式之中断】

Cortex-M4集成了嵌套式矢量型中断控制器(Nested Vectored Interrupt Controller (NVIC))来实现高效的异常和中断处理。NVIC实现了低延迟的异常和中断处理&#xff0c;以及电源管理控制。它和内核是紧密耦合的。 凡是打断程序顺序执行的事件都称为异常&#xff08;exception&am…

Gitlab: Python项目CI/CD实践

目录 1. 说明 2. 准备工作 2.1 服务器 2.2 开发机hosts文件 2.3 项目 3. 步骤过程 3.1 建仓Fastapi T1 3.2 开发机测试构建与推送 ​编辑 3.3 在工作站添加gitlab-runner 3.4 提交代码&#xff0c;查看Pipelines结果 3.5 观察部署情况 4. 参考 1. 说明 分别以一个…

线程互斥及基于线程锁的抢票程序

我们实现一个简单的多线程抢票程序。 #include<iostream> #include<thread> #include<unistd.h> #include<functional> #include<vector> using namespace std; template<class T> using func_tfunction<void(T)>;//返回值为void,…

leetcode刷题(python)——(四)

01.02.03 练习题目&#xff08;第 04 天&#xff09; 1. 0048. 旋转图像 1.1 题目大意 描述&#xff1a;给定一个 n n n \times n nn 大小的二维矩阵&#xff08;代表图像&#xff09; m a t r i x matrix matrix。 要求&#xff1a;将二维矩阵 m a t r i x matrix matr…

点云的投影------PCL

点云的投影 /// <summary> /// 参数化模型投影点云 /// </summary> /// <param name"cloud">点云</param> /// <param name"x">投影平面x面的系数</param> /// <param name"y"></param> /// &…

【Qt-Qt Creator使用技巧】

工具-Qt Creator ■ 使用技巧■ 定义触发片段■ Qt Creator 行编辑■ 代码注释■ 代码补全■ 快速给函数添加定义■ 创建书签■ 同步列输入■ 局部替换■ 源代码阅读■ 源码调试■ 使用技巧 ■ 定义触发片段 ■ Qt Creator 行编辑 shift + alt + up / down来获得多个游标。 …

第二部分 Python提高—GUI图形用户界面编程(三)

简单组件学习 Radiobutton 单选按钮、Checkbutton 复选按钮和canvas 画布 文章目录 Radiobutton 单选按钮Checkbutton 复选按钮canvas 画布 Radiobutton 单选按钮 Radiobutton 控件用于选择同一组单选按钮中的一个。Radiobutton 可以显示文本&#xff0c;也可以显示图像。 f…

CUDA编程---线程束洗牌指令

从Kepler系列的GPU&#xff08;计算能力为3.0或更高&#xff09;开始&#xff0c;洗牌指令&#xff08;shuffle instruction&#xff09;作为一种机制被加入其中&#xff0c;只要两个线程在相同的线程束中&#xff0c;那么就允许这两个线程直接读取另一个线程的寄存器。 洗牌指…