C++数据结构X篇_04_单向链表框架搭建、实现和测试(链表的定义,常用操作的实现等)

news2024/9/27 19:26:31

接上篇C++数据结构X篇_03_线性表的顺序存储和动态数组案例(基本概念;操作要点;顺序存储算法;动态数组案例实现),本篇将会开始介绍线性表的链式存储。
参考博文:最详细的C++单向链表实现,文中采用C++的class去实现单向链表,这与本文采用struct的形式是类似的,本文为了与学习视频一致,仍采用struct的形式介绍单向链表。

单向链表框架搭建、实现和测试

  • 1 基本概念
    • 1.1 链式存储定义
    • 1.2 单向链表
      • 1.2.1单向链表概念
      • 1.2.2 链表插入
      • 1.2.3 链表删除
  • 2 代码实现
    • 2.1 单向链表框架搭建(.h文件)
    • 2.2 单向链表框架实现(.cpp文件)
    • 2.3 单向链表测试(客户测试)

1 基本概念

1.1 链式存储定义

为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息。
在这里插入图片描述

1.2 单向链表

链表是数据结构中常见的一种数据存储形式,其在物理存储单元上是非连续的存储结构,即其数据存储的空间并非连续的内存空间。这种存储方式的优点是可以动态分配内存空间,不会造成内存的浪费和溢出,同时链表对于执行插入、删除等操作十分简便,不需要移动大量元素,但是其相对于连续空间存储的数据相比,遍历速度较慢。

1.2.1单向链表概念

在这里插入图片描述
链表由多个node(节点)组成,每个节点中由数据域与指针域组成,数据域data中存储需要存储的数据,指针域next为一个指针,其指向下一个node的地址。
当多个node前后链接在一起时就形成了lis(链表):
在这里插入图片描述
list中每个node通过指针next前后相联系,但要注意list中最后一个node由于没有后续的node,因此其next指正是指向NULL的。

1.2.2 链表插入

在这里插入图片描述
对于一个链表list若想把Node n插入Node 0与Node 1之间,只需要将Node 0的next指针指向Node n的地址,再把Node n的next指针指向Node 1的地址即可。

1.2.3 链表删除

在这里插入图片描述
若想删除节点Node 2,只需要将其前一节点Node 1的next指针指向其后一节点Node 3的地址,再将Node 2的内存空间delete掉即可。

2 代码实现

以下是在VS IDE中已经有一个项目的情况下如何增加一个新的项目“ListTest2”
在这里插入图片描述
与上篇一致,此部分将会按照搭建,实现,测试进行介绍

2.1 单向链表框架搭建(.h文件)

#pragma once
#ifndef LINKLIST_H
#define LINKLIST_H

//list是由节点组成,先定义一个链表节点
typedef struct LINKNODE
{
	void* data;  //无类型指针,指向任何类型数据
	struct LINKNODE* next;
}LinkNode;

//链表结构体
typedef struct LINKLIST 
{
	LinkNode* head;
	//无需容量,链表是来一个节点就申请一个内存,据需申请内存
	int size;
}LinkList;

//打印回调函数指针,返回是void
typedef void(*PRINTLINKNODE)(void*);

//初始化链表
LinkList* Init_LinkList();
//指定位置插入
void Insert_LinkList(LinkList* list,int pos,void* data);
//删除指定位置的值
void RemoveByPos_LinkList(LinkList* list,int pos);
//获得链表的长度
int Size_LinkList(LinkList* list);
//查找
int Find_LinkList(LinkList* list,void* data);
//打印链表节点
//用户可能传入任何数据类型,但是传进后都会转成void*
//我们无法知道用户传入的是什么类型,不确定的时候让用户去做
//给函数,遍历的时候将void*数据类型传给函数,让用户打印
void Print_LinkList(LinkList* list, PRINTLINKNODE print);
//返回第一个节点
void* Front_LinkList(LinkList* list);
//释放链表内存
void FreeSpace_LinkList(LinkList* list);

#endif

2.2 单向链表框架实现(.cpp文件)

#include "LinkList.h"
#include<iostream>
#pragma once

//初始化链表
LinkList * Init_LinkList()
{
	LinkList * list = (LinkList*)malloc(sizeof(LinkList));
	list->size = 0;
	//头结点 不保存数据信息 为了实现链表下少考虑几种情况
	//比如在插入时少考虑是否插入头部
	list->head = (LinkNode *)malloc(sizeof(LinkNode));
	list->head->data = NULL;
	list->head->next = NULL;

	return list;
}
//指定位置插入
void Insert_LinkList(LinkList * list, int pos, void * data)
{
	if (list==NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}
	//友好的处理,pos越界,默认插入到尾部
	if (pos< 0 || pos>list->size)
	{
		pos = list->size;
	}

	//创建新的节点
	LinkNode* newnode = (LinkNode*)malloc(sizeof(LinkNode));
	newnode->data = data;
	newnode->next = NULL;

	//找节点
	//辅助指针变量
	LinkNode* pCurrent = list->head;
	for (int i=0;i<pos;i++)
	{
		pCurrent = pCurrent->next;
	}

	//新节点入链表
	newnode->next = pCurrent->next; //pCurrent->next表示pos-1位置的下一个地址,也就是pos位置地址
	pCurrent->next = newnode;

	list->size++;

}
//删除指定位置的值
void RemoveByPos_LinkList(LinkList * list, int pos)
{
	if (list == NULL)
	{
		return;
	}

	if (pos< 0 || pos>= list->size)
	{
		return;
	}

	//查找删除节点的前一个节点
	LinkNode* pCurrent = list->head;
	for (int i = 0; i < pos; i++)
	{
		pCurrent = pCurrent->next;
	}

	//缓存删除的节点
	LinkNode* pDel = pCurrent->next;
	pCurrent->next = pDel->next;

	//释放删除节点的内存
	free(pDel);

	list->size--;
}
//获得链表的长度
int Size_LinkList(LinkList * list)
{
	return list->size;
}
//查找
int Find_LinkList(LinkList * list, void * data)
{
	if (list == NULL)
	{
		return 0;
	}
	if (data == NULL)
	{
		return 0;
	}
	//遍历查找 head不保存有效数据 list->head->next指向第一个有效数据
	LinkNode* pCurrent = list->head->next;
	int i = 0;
	while (pCurrent!=NULL)
	{
		if (pCurrent->data == data)
		{
			break;
		}
		i++;
		pCurrent = pCurrent->next;
	}

	return i;
}
//打印链表节点
void Print_LinkList(LinkList * list, PRINTLINKNODE print)
{
	if (list == NULL)
	{
		return;
	}

	//辅助指针变量
	LinkNode* pCurrent = list->head->next;
	while (pCurrent != NULL)
	{
		print(pCurrent->data);
		pCurrent = pCurrent->next;
	}

}
//返回第一个节点
void * Front_LinkList(LinkList * list)
{
	return list->head->next->data;
}
//释放链表内存
void FreeSpace_LinkList(LinkList * list)
{
	if (list == NULL)
	{
		return;
	}

	//每一个节点都需要手动释放
	//辅助指针变量
	LinkNode* pCurrent = list->head;
	//不能找到一个节点就进行释放,需要先缓存下一个节点,然后删除当前节点
		while (pCurrent != NULL)
		{
         //缓存下一个节点
			LinkNode* pNext = pCurrent->next;

			free(pCurrent);

			pCurrent = pNext;
		}

		//释放链表内存
		list->size = 0;
		free(list);
}

2.3 单向链表测试(客户测试)



#include <iostream>
#include "LinkList.h"

//自定义数据类型
typedef struct PERSON {
	char name[64];
	int age;
	int score;
} Person;

//打印函数
void MyPrint(void* data) {
	Person* p = (Person*)data;
	printf("Name:%s Age:%d Score:%d\n",p->name,p->age,p->score);
}

int main()
{
    //创建链表
	LinkList* list = Init_LinkList();

	//创建数据
	Person p1 = {"aaa",18,100};
	Person p2 = { "bbb",19,99 };
	Person p3 = { "ccc",20,101 };
	Person p4 = { "ddd",17,97 };
	Person p5 = { "eee",16,59 };

	//数据插入链表
	Insert_LinkList(list,0,&p1);
	Insert_LinkList(list, 0, &p2);
	Insert_LinkList(list, 0, &p3);
	Insert_LinkList(list, 0, &p4);
	Insert_LinkList(list, 0, &p5);

	//打印 打印顺序为p5 p4 p3...
	Print_LinkList(list,MyPrint);

	//删除3 也就是从p5往前从0数到3,即为p2
	RemoveByPos_LinkList(list, 3);

	//增加分隔符之后进行打印
	printf("---------------------\n");
	Print_LinkList(list, MyPrint);

	//返回第一个节点
	printf("---------查找结果------------\n");
	 Person* ret= (Person*)Front_LinkList(list);
	 printf("Name:%s Age:%d Score:%d\n", ret->name, ret->age, ret->score);

	//销毁链表
	FreeSpace_LinkList(list);

}

测试结果:
在这里插入图片描述

  1. 参考视频地址:单向链表框架搭建单向链表框架实现,单向链表测试

  2. 上述源代码见:博文C++数据结构X篇-04-单向链表框架搭建、实现和测试(链表的定义,常用操作的实现等)的配套资源

  3. 最详细的C++单向链表实现的代码如下:


#include <iostream>
using namespace std;

class node 
{
public:
	int data; //存储数据
	node* next; //指向下一个node的地址
};

class list 
{
public:
	node* head; //指向链表首位node
	int size;   //链表中的node的个数
};

//链表初始化
list* list_init()
{
	list* L = new list; //创建一个链表L
	L->size = 0; //初始node个数设置为0
	L->head = new node; //创建链表中的第一个node
	L->head->data = NULL; //将第一个node中存储的data设为空
	L->head->next = NULL; //将第一个node中存储的next指针设为空
	return L;
}

//链表的数据插入
void list_insert(list* L,int pos,int data)
{
	//如果数据为空,则从头插入
	if (L == NULL)
	{
		pos = 0;
	}

	//创建新节点并把数据放入
	node* new_node = new node;
	new_node->data = data;
	new_node->next = NULL;

	//寻找pos-1号的位置
	node* pos_node = L->head; //0号node
	for (int i = 0; i < pos; i++)
	{
		//将下一个node的地址赋给pos_node地址,相当于地址在不断向后移动
		pos_node = pos_node->next; //获得pos-1号node(pos_node)
	}

	//新节点入链表
	new_node->next = pos_node->next; //将new_node的next指向第pos号的node地址
	pos_node->next = new_node; //pos-1的node指向new_node

	L->size++;

}

//链表的数据删除
void list_remove_pos(list* L,int pos)
{
	//判断链表是否为空
	if (L == NULL)
	{
		return;
	}
	
	//找到需要删除的前一个节点
	node* pos_node = L->head;
	for (int i = 0; i < pos; i++)
	{
		pos_node = pos_node->next;
	}

	//删除pos的节点
	pos_node->next = pos_node->next->next;
	L->size--;
}

//打印链表
void list_print(list* L)
{
	if (L == NULL)
	{
		cout << "链表中没有数据" << endl;
	}

	//遍历打印
	node* pcurrent = L->head->next;
	while(pcurrent !=NULL)
	{
		cout << pcurrent->data << endl;
		pcurrent = pcurrent->next;
	}
	cout << endl;
}
int main()
{
    //创建链表
	list* L = list_init();
	//插入数据
	for (int i=0;i<10;i++)
	{
		list_insert(L, i, i);
	}
	cout << "插入0-9的链表为:" << endl;
	list_print(L);

	//删除指定位置
	list_remove_pos(L, 5);
	cout << "删除第5号node后的地址为:" << endl;
	list_print(L);
	system("pause");
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

细胞衰老β-半乳糖苷酶染色试剂盒丨艾美捷解决方案

细胞衰老&#xff08;Cell senescence&#xff09;是细胞控制其生长潜能的保障机制&#xff0c;一般含义是复制衰老&#xff08;Replicative senescence&#xff0c;RS&#xff09;&#xff0c;指正常细胞经过有限次数的分裂后&#xff0c;停止分裂&#xff0c;此时细胞虽然是存…

DSP篇--C6678功能调试系列之EMIF、GPIO调试

目录 1、EMIF调试 2、GPIO调试 前言不用多说&#xff0c;详见DSP篇--C6678功能调试系列之DDR3调试_nanke_yh的博客-CSDN博客 1、EMIF调试 EMIF主要是提供挂载的NOR FLASH/NAND FLASH/**RAM上的时序。 EMIF16 can operate in the following modes: • WE Strobe Mode • Sele…

【元胞自动机】元胞自动机求解城市小区开放对周边道路通行影响研究【含Matlab源码 233期】

⛄一、元胞自动机简介 1 元胞自动机发展历程 最初的元胞自动机是由冯 诺依曼在 1950 年代为模拟生物 细胞的自我复制而提出的. 但是并未受到学术界重视. 1970 年, 剑桥大学的约翰 何顿 康威设计了一个电脑游戏 “生命游戏” 后, 元胞自动机才吸引了科学家们的注意. 1983 年…

安全-加密与证书

对称加密 在对称加密中&#xff0c;加密和解密使用的是同一个密钥&#xff0c;即&#xff1a;使用相同的密钥对密文进行加密和解密 比如&#xff1a;A和B&#xff0c;A和B保存同一个密钥&#xff0c;A使用这个密钥对明文进行加密&#xff0c;发送给B&#xff0c;B再使用这个密…

【火灾检测】森林火灾检测系统(带面板)【含GUI Matlab源码 1921期】

⛄一、火灾检测简介 1 引言 目前森林火灾是破坏森林的最主要的灾害之一, 影响很大。森林是各种珍禽异兽的家园, 森林遭受火灾后, 会破坏野生动物赖以生存的环境。严重的森林火灾不仅能引起水土流失, 还会引起山洪爆发、泥石流等自然灾害。因此, 对森林火灾尽早识别并预警, 就能…

CSS 实现跳动的方块动画

前言 &#x1f44f;transform-styletransform实现多个小方块&#xff0c;速速来Get吧~ &#x1f947;文末分享源代码。记得点赞关注收藏&#xff01; 1.实现效果 2.实现步骤 定义css变量&#xff1a;正方形长/宽为w&#xff1b; :root {--w: 30px;}父容器为一个圆角正方形&…

引擎入门 | Unity UI简介–第2部分(3)

本期我们继续为大家进行Unity UI简介&#xff08;第二部分&#xff09;的后续教程 本篇内容 4.设置动画按钮 5.从脚本中触发动画按钮 文章末尾可免费获取教程源代码 本篇本篇Unity UI简介&#xff08;第二部分&#xff09;篇幅较长&#xff0c;分为八篇&#xff0c;本篇为…

Android Gradle 学习笔记(一)概述

文章目录1. JVM构建工具的发展1.1 背景1.2 Ant - Java 早期构建工具1.3 Maven - Ant 的升级版1.3 Gant - IDEA 官方的构建工具1.4 Gradle - JVM集大成构建工具2. 为什么学习 Gradle?3. 学习提纲参考1. JVM构建工具的发展 1.1 背景 我们平时在 IDE 上写了很多代码&#xff0c…

vue3+Element-plus el-select 下拉表格组件(el-select+el-table结合)

一、最终效果 二、代码示例 <t-select-table:table"table":columns"table.columns":max-height"400":keywords"{ label: name, value: id }"radioChange"radioChange" ></t-select-table>三、参数配置 1. 配置…

如何开始用Python编程

前言 你想开始学习如何编程吗&#xff1f;计算机编程令人望而生畏&#xff0c;你可能认为需要通过上课来学习。虽然对于某些语言来说可能是这样&#xff0c;但是有很多编程语言只需一到两天的时间就可以掌握基础知识。Python[1] 就是这样的一种语言。你在几分钟内就可以正常运…

【操作系统基础】实践部分

本文参考MOOC哈工大操作系统课程与课件 主要基于Linux 0.11系统展开 ”Author&#xff1a;Mayiming“ 实践部分依赖虚拟环境展开&#xff0c;请访问网址 https://www.lanqiao.cn/courses/115 本文就试验一、二、三进行梳理 一、熟悉试验环境 试验环境使用了oslab、bochs、gcc…

使用图片制作3D背景

1.创建一个新的摄像机&#xff0c;命名为BackgroundCamera。 2.新建GUITexture&#xff0c;命名为BackgroundImage。 3.在BackgroundImage的Inspector面板中点击Layer下拉窗口&#xff0c;选择“AddLayer”。 4.在打开的面板中的UserLayer8&#xff0c;添加一个新的层名称为…

dubbo原理

目录 dubbo原理 1、RPC原理 2、netty通信原理 3、dubbo原理 1、dubbo原理 -框架设计 2、dubbo原理 -启动解析、加载配置信息 3、dubbo原理 -服务暴露 4、dubbo原理 -服务引用 5、dubbo原理 -服务调用 dubbo原理 1、RPC原理 一次完整的RPC调用流程&#xff08;同步调…

软件测试培训之写给要学习自动化测试的同学的建议

基于我的经验&#xff0c;给你6条实用建议 1、先学习编程语言&#xff0c;然后再接触自动化工具。 语言选择上Java或者Python都是可以的&#xff0c;可以先从Python入手&#xff0c;之后再开始Java。在学习语言的过程中&#xff0c;一定要忘掉你是做测试的&#xff0c;把自己…

leetcode 698. 划分为k个相等的子集-状态压缩+记忆搜索的一步步实现

题目 给定一个整数数组 nums 和一个正整数 k&#xff0c;找出是否有可能把这个数组分成 k 个非空子集&#xff0c;其总和都相等。 示例 输入&#xff1a; nums [4, 3, 2, 3, 5, 2, 1], k 4 输出&#xff1a; True 说明&#xff1a; 有可能将其分成 4 个子集&#xff08;5&…

利用OpenCV的函数LUT()对矩阵的数据进行查表映射

利用OpenCV的函数LUT()对矩阵的数据进行查表映射 LUT是Look Up Table 的缩写&#xff0c;意为查表映射。 OpenCV的函数LUT()能实现图像灰度值或者说矩阵元素值的查表映射功能。 函数LUT()的C原型如下&#xff1a; void cv::LUT(InputArray src,InputArray lut,OutputArray …

XStream常用注解学习

XStream中文教程&#xff1a;https://www.wenjiangs.com/doc/iyx6stww 参考博客&#xff1a;https://www.jb51.net/article/201309.htm 用在xml中&#xff0c;常用注解&#xff1a; XStreamAliasType(value“要修改成的全限定名”): 包名修改 XStreamAlias(“user”) : 修改类,…

力扣hot100——第5天:22括号生成、23合并K个升序链表、31下一个排列

文章目录1.22括号生成1.1.题目1.2.题解2.23合并K个升序链表2.1.题目2.2.解答3.31下一个排列3.1.题目3.2.解答1.22括号生成 参考&#xff1a;力扣题目链接&#xff1b;题解1&#xff0c;题解2 1.1.题目 1.2.题解 这道题目是使用递归的方法来求解&#xff0c;因为要求解所有的…

这个macOS神器,让爱怀旧的人直呼:“爷青回!”

写在前面 Hello&#xff0c;大家好&#xff0c;我们又见面了。 停止更新了两周多&#xff0c;本来打算荒废这个CSDN的&#xff0c;但对写文章的热爱又逼着我继续写…… 这次我们要推荐一个macOS神器&#xff0c;叫“Aqua Menu Bar”。 以后永远不写水文了&#xff0c;告别CS…

AJAX异步请求解决跨域问题的三种方式

一 什么是跨域 出于浏览器的同源策略限制。同源策略&#xff08;Sameoriginpolicy&#xff09;是一种约定&#xff0c;它是浏览器最核心也最基本的安全功能&#xff0c;如果缺少了同源策略&#xff0c;则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的…