【数据结构】单链表的使用

单链表的使用

    • 1、基本概念
    • 2、链表的分类
    • 3、链表的基本操作
      • a、单链表节点设计
      • b、单链表初始化
      • c、单链表增删节点
        • **节点头插:**
        • **节点尾插:**
        • **新节点插入指定节点后:**
        • 节点删除:
      • d、单链表修改节点
      • e、单链表遍历,并打印数据
    • 4、链表优缺点
    • 5、完整示例代码

1、基本概念

  • 顺序表:顺序存储的线性表。
  • 链式表:链式存储的线性表,简称链表。
    既然顺序存储中的数据因为挤在一起而导致需要成片移动,那很容易想到的解决方案是将数据离散地存储在不同内存块中,然后在用来指针将它们串起来。这种朴素的思路所形成的链式线性表,就是所谓的链表。

顺序表和链表在内存在的基本样态如下图所示:

  • 顺序表:

在这里插入图片描述
链表:
在这里插入图片描述

2、链表的分类

\quad 根据链表中各个节点之间使用指针的个数,以及首尾节点是否相连,可以将链表细分为如下种类:
1.单向链表
2.单向循环链表
3.双向循环链表
\quad 这些不同链表的操作都是差不多的,只是指针数目的异同。以最简单的单向链表为例,其基本示意图如下所示:
在这里插入图片描述上图中,所有的节点均保存一个指针,指向其逻辑上相邻的下一个节点(末尾节点指向空)。
另外注意到,整条链表用一个所谓的头指针 head 来指向,由 head 开始可以找到链表中的任意一个节点。head 通常被称为头指针

3、链表的基本操作

  1. 节点设计
  2. 初始化空链表
  3. 增删节点
  4. 链表遍历(改查)
  5. 销毁链表

下面着重针对这几项常见操作,讲解单向链表的处理。

a、单链表节点设计

单向链表的节点非常简单,节点中除了要保存用户数据之外(这里以整型数据为例),只需要增加一个指向本类节点的指针即可,如下所示:

typedef struct single_list{// 数据域--》真实的数据int data;    				// 以整型数据为例// 指针域struct single_list *next; 	// //用来指向下一个元素在内存中的首地址
}single_list_t;

b、单链表初始化

\quad 首先,空链表有两种常见的形式。一种是带头结点的,一种是不带头结点的。所谓的头结点是不存放有效数据的节点,仅仅用来方便操作,如下:
在这里插入图片描述
而不带头结点的空链表如下所示:
在这里插入图片描述
\quad 由于头结点是不存放有效数据的,因此如果空链表中带有头结点,那么头指针 head 将永远不变,这会给以后的链表操作带来些许便捷。
下面以带头结点的链表为例,展示单向链表的初始化的示例代码:

single_list_t *single_list_init()
{single_list_t *head_node = malloc(sizeof(single_list_t));if (head_node != NULL){head_node->next = NULL;}return head_node;
}

c、单链表增删节点

\quad 相对于顺序表需要整片移动数据,链表增删节点只需要修改几个相关指针的指向,动作非常快速。
\quad 与顺序表类似,可以对一条链表中的任意节点进行增删操作,示例代码:

节点头插:
int insert_list_head(int newdata, single_list_t *list)
{// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// 插入节点new_node->next = list->next;list->next = new_node;return 0;
}
节点尾插:
int insert_list(int newdata, single_list_t *list)
{// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// 定义指针p去找到链表的尾部single_list_t *p = list;while (p->next != NULL){p = p->next;}// 此时p已经到最后一个节点的位置p->next = new_node;return 0;
}
新节点插入指定节点后:
int insert_list_mid(int olddata, int newdata, single_list_t *list)
{// 找到要插入的节点single_list_t *p = list;while (p->next != NULL){p = p->next;if (p->data == olddata) // 待插入的节点在链表中间{break;}}// 申请一个新的节点 single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// p指向最后一个节点if (p->next == NULL){// 最后一个节点是要插入的节点if (p->data == olddata){new_node->next = p->next; // 先让新增加的节点指向找到节点的下一个节点p->next = new_node;   // 再将该节点指向新增加的节点}else{printf("要插入的数据不存在\n");return -1;}}else // 遍历到中间找到需要插入的节点{new_node->next = p->next; // 先让新增加的节点指向找到节点的下一个节点p->next = new_node;   // 再将该节点指向新增加的节点}return 0;
}
节点删除:
int list_delnode(int deldata, single_list_t *list)
{single_list_t *q = list;single_list_t *p = list->next;while (p->next != NULL){// 找到要删除的节点并进行删除if (p->data == deldata){q->next = p->next;  // 将q指向的节点指向被删除节点的下一个节点p->next = NULL;     // p节点的next指针指向NULLfree(p);    // 释放p后此时p是野指针p = q->next;// 将p指向被删除节点的下一个节点}else{p = p->next;q = q->next;}  }// 遍历到最后一个节点if (p->next == NULL){// 若最后一个节点是要删除的节点,则删除if (p->data == deldata){q->next = p->next;p->next = NULL;free(p);}else{printf("最后一个节点不是要删除的节点\n");return 0;}}
}

d、单链表修改节点

int list_update_node(int old_data, int new_data, single_list_t *list)
{single_list_t *p = list;while (p->next != NULL){p = p->next;  // p指向下一个节点if (p->data == old_data){p->data = new_data;}}return 0;
}

e、单链表遍历,并打印数据

int list_show(single_list_t *list)
{single_list_t *p = list;while (p->next != NULL){p = p->next;printf("当前p指向的节点数据:%d\n", p->data);}
}

4、链表优缺点

\quad 链式存储中,所有节点的存储位置是随机的,他们之间的逻辑关系用指针来确定,跟物理存储位置无关,因此从上述示例代码可以很清楚看到,增删数据都非常迅速,不需要移动任何数据。另外,又由于位置与逻辑关系无关,因此也无法直接访问某一个指定的节点,只能从头到尾按遍历的方式一个个找到想要的节点。简单讲,链式存储的优缺点跟顺序存储几乎是相对的。
总结其特点如下:

  • 优点
    a.插入、删除时只需要调整几个指针,无需移动任何数据
    b.当数据节点数量较多时,无需一整片较大的连续内存空间,可以灵活利用离散的内存
    c.当数据节点数量变化剧烈时,内存的释放和分配灵活,速度快
  • 缺点
    a.在节点中,需要多余的指针来记录节点之间的关联
    b.所有数据都是随机存储的,不支持立即访问任意一个随机数据。

5、完整示例代码

#include <stdio.h>
#include <stdlib.h>// 1.封装一个结构体来表示单链表
typedef struct single_list{int data;struct single_list *next;
}single_list_t;// 2.初始化单链表-->定义结构体变量 创建头节点
single_list_t *single_list_init()
{single_list_t *head_node = malloc(sizeof(single_list_t));if (head_node != NULL){head_node->next = NULL;}return head_node;
}// 头插
int insert_list_head(int newdata, single_list_t *list)
{// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// 插入节点new_node->next = list->next;list->next = new_node;return 0;
}
/*@brief:3.插入数据-->尾插(在最后一个有效成员的后面插入数据)*@param(in):  newdata :待插入的数据  list:待插入的链表*@param(out):  *@retval:    
*/
int insert_list(int newdata, single_list_t *list)
{// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// 定义指针p去找到链表的尾部single_list_t *p = list;while (p->next != NULL){p = p->next;}// 此时p已经到最后一个节点的位置p->next = new_node;return 0;
}// 中间插入
int insert_list_mid(int olddata, int newdata, single_list_t *list)
{// 找到要插入的节点single_list_t *p = list;while (p->next != NULL){p = p->next;if (p->data == olddata){break;}}// 申请一个节点 -堆空间single_list_t *new_node = malloc(sizeof(single_list_t));// 初始化数据域new_node->data = newdata;new_node->next = NULL;// p指向最后一个节点if (p->next == NULL){if (p->data == olddata){new_node->next = p->next; // 先让新增加的节点指向找到节点的下一个节点p->next = new_node;   // 再将该节点指向新增加的节点}else{printf("要插入的数据不存在\n");return -1;}}else // 遍历到中间找到需要插入的节点{new_node->next = p->next; // 先让新增加的节点指向找到节点的下一个节点p->next = new_node;   // 再将该节点指向新增加的节点}return 0;
}
// 删除节点
int list_delnode(int deldata, single_list_t *list)
{single_list_t *q = list;single_list_t *p = list->next;while (p->next != NULL){// 找到要删除的节点并进行删除if (p->data == deldata){q->next = p->next;  // 将q指向的节点指向被删除节点的下一个节点p->next = NULL;     // p节点的next指针指向NULLfree(p);    // 释放p后此时p是野指针p = q->next;// 将p指向被删除节点的下一个节点}else{p = p->next;q = q->next;}  }// 遍历到最后一个节点if (p->next == NULL){// 若最后一个节点是要删除的节点,则删除if (p->data == deldata){q->next = p->next;p->next = NULL;free(p);}else{printf("最后一个节点不是要删除的节点\n");return 0;}}
}
// 修改节点
int list_update_node(int old_data, int new_data, single_list_t *list)
{single_list_t *p = list;while (p->next != NULL){p = p->next;  // p往后移动if (p->data == old_data){p->data = new_data;}}return 0;
}// 4.遍历链表,打印节点数据
int list_show(single_list_t *list)
{single_list_t *p = list;while (p->next != NULL){p = p->next;printf("当前p指向的节点数据:%d\n", p->data);}
}int main(int argc, char const *argv[])
{// 初始化单链表single_list_t *my_list = single_list_init();// 往链表插入数据insert_list(15, my_list);insert_list(18, my_list);insert_list(15, my_list);insert_list(25, my_list);insert_list(666, my_list);insert_list(123, my_list);insert_list(11111111, my_list);insert_list_mid(666, 777, my_list);insert_list_mid(1, 222, my_list);insert_list_head(15, my_list);printf("============插入的节点============\n");list_show(my_list);printf("============插入的节点============\n");// 删除节点list_delnode(25,my_list);printf("============删除后的节点============\n");list_show(my_list); // 打印数据printf("============删除后的节点============\n");// 修改数据list_update_node(123, 234, my_list);printf("============修改后的节点============\n");list_show(my_list); // 打印数据printf("============修改后的节点============\n");return 0;
}
/*
执行结果:
要插入的数据不存在
============插入的节点============
当前p指向的节点数据:15
当前p指向的节点数据:15
当前p指向的节点数据:18
当前p指向的节点数据:15
当前p指向的节点数据:25
当前p指向的节点数据:666
当前p指向的节点数据:777
当前p指向的节点数据:123
当前p指向的节点数据:11111111
============插入的节点============
最后一个节点不是要删除的节点
============删除后的节点============
当前p指向的节点数据:15
当前p指向的节点数据:15
当前p指向的节点数据:18
当前p指向的节点数据:15
当前p指向的节点数据:666
当前p指向的节点数据:777
当前p指向的节点数据:123
当前p指向的节点数据:11111111
============删除后的节点============
============修改后的节点============
当前p指向的节点数据:15
当前p指向的节点数据:15
当前p指向的节点数据:18
当前p指向的节点数据:15
当前p指向的节点数据:666
当前p指向的节点数据:777
当前p指向的节点数据:234
当前p指向的节点数据:11111111
============修改后的节点============
*/

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

浅谈某平台多场景下反爬虫与风控业务

文章目录 1. 写在前面2. 内容反爬3. 账号风控3. 接口验签 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致…

如何在网页端使用 IDE 高效地阅读 GitHub 源码?

如何在网页端使用 IDE 高效地阅读 GitHub 源码&#xff1f; 前言什么是 GitHub1s&#xff1f;使用 GitHub1s 阅读 browser-use 项目源码步骤 1: 打开 GitHub 项目页面步骤 2: 修改 URL 使用 GitHub1s步骤 3: 浏览文件结构步骤 4: 使用代码高亮和智能补全功能步骤 5: 快速跳转和…

Web Bluetooth API 开发记录

搞了一天的蓝牙串口协议被几个软件和AI带沟里面去了。 1.00001101-0000-1000-8000-00805f9b34fb 是spp协议。但是我用的称是使用的49535343-fe7d-4ae5-8fa9-9fafd205e455蓝牙低功耗spp协议 2.推荐一款软件Android-nRF-Connect github地址&#xff1a;https://github.com/Nor…

使用VS Code开发ThinkPHP项目

【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《ThinkPHP 8高效构建Web应用 夏磊 编程与应用开发丛书 清华大学出版社》【摘要 书评 试读】- 京东图书 ThinkPHP 8开发环境安装-CSDN博客 安装ThinkPHP项目的IDE 常用的集成开发环境&#xff08;IDE&#xff09;包括P…

开源轻量级文件分享服务Go File本地Docker部署与远程访问

???欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学习,不断总结,共同进步,活到老学到老…

Windows上缺少xaudio2_9.dll是什么原因?

一、文件丢失问题&#xff1a;Windows上缺少xaudio2_9.dll是什么原因&#xff1f; xaudio2_9.dll是DirectX音频处理库的一个组件&#xff0c;它支持游戏中的音频处理功能。当你在Windows系统上运行某些游戏或音频软件时&#xff0c;如果系统提示缺少xaudio2_9.dll文件&#xf…

缓存管理自动化:JuiceFS 企业版 Cache Group Operator 新特性发布

近期&#xff0c;JuiceFS 企业版推出了 Cache Group Operator&#xff0c;用于自动化创建和管理缓存组集群。Operator 是一种简化 Kubernetes 应用管理的工具&#xff0c;它能够自动化应用程序的生命周期管理任务&#xff0c;使部署、扩展和运维更加高效。 在推出 Operator 之前…

SCSA:探索空间与通道注意力之间的协同效应

文章目录 摘要1 引言2 相关工作2.1 多语义空间信息2.2 注意力分解 3 方法3.1 共享多语义空间注意力&#xff1a;空间与通道分解3.2 渐进式通道自注意力3.3 协同效应3.4 注意力机制的整合 4 实验4.1 实验设置4.2 图像分类4.3 目标检测4.4 分割4.5 消融研究 5 可视化与分析5.1 注…

Grok 2.0:马斯克的大模型挑战ChatGPT,AI竞争再升级

引言&#xff1a;马斯克Grok 2.0的横空出世 在人工智能&#xff08;AI&#xff09;领域&#xff0c;竞争从未停止。随着大型语言模型&#xff08;LLM&#xff09;的快速发展&#xff0c;各大科技巨头纷纷推出自己的AI模型&#xff0c;试图在激烈的竞争中占据领先地位。最近&am…

基于Spring Boot的宠物领养系统的设计与实现(代码+数据库+LW)

摘 要 如今社会上各行各业&#xff0c;都在用属于自己专用的软件来进行工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。互联网的发展&#xff0c;离不开一些新的技术&#xff0c;而新技术的产生往往是为了解决现有问题而产生的。针对于宠物领…

安卓15预置第三方apk时签名报错问题解决

有同事反馈集成apk时安装失败 PackageManager: Failed to scan /product/app/test: No APK Signature Scheme v2 signature in package /product/app/test/test.apk 查看编译后的apk签名信息 DOES NOT VERIFY ERROR: JAR signer CERT.RSA: JAR signature META-INF/CERT.SF indi…

从0入门自主空中机器人-2-1【无人机硬件框架】

关于本课程&#xff1a; 本次课程是一套面向对自主空中机器人感兴趣的学生、爱好者、相关从业人员的免费课程&#xff0c;包含了从硬件组装、机载电脑环境设置、代码部署、实机实验等全套详细流程&#xff0c;带你从0开始&#xff0c;组装属于自己的自主无人机&#xff0c;并让…

实现某海外大型车企(T)Cabin Wi-Fi 需求的概述 - 4

大家好&#xff0c;我是Q&#xff0c;邮箱&#xff1a;1042484520qq.com。 今天我们在上几讲的基础上再扩展下 Cabin Wi-Fi 的功能需求&#xff0c;讲讲如何使能 5G TCU Wi-Fi STA Bridge 模式。 参考&#xff1a; 实现某海外大型车企&#xff08;T&#xff09;Cabin Wi-Fi 需求…

2024 年最新 windows 操作系统搭建部署 nginx 服务器应用详细教程(更新中)

nginx 服务器概述 Nginx 是一款高性能的 HTTP 和 反向代理 服务器&#xff0c;同时是一个 IMAP / POP3 / SMTP 代理服务器。Nginx 凭借其高性能、稳定性、丰富的功能集、简单的配置和低资源消耗而闻名。 浏览 nginx 官网&#xff1a;https://nginx.org/ Nginx 应用场景 静态…

C 实现植物大战僵尸(二)

C 实现植物大战僵尸&#xff08;二&#xff09; 前文链接&#xff0c;C 实现植物大战僵尸&#xff08;一&#xff09; 五 制作启动菜单 启动菜单函数 void startUI() {IMAGE imageBg, imgMenu1, imgMenu2;loadimage(&imageBg, "res/menu.png");loadimage(&am…

Android笔记(四十一):TabLayout内的tab不滚动问题

背景 假设二级页面是上面图片的布局&#xff0c;当进来时TabLayout和ViewPager2绑定完就马上调setCustomItem&#xff0c;跳转到最后一个tab页面时&#xff0c;会发现tab不滚动&#xff0c;手动滑一下ViewPager2时才会滚动tab到正确的位置 原因分析 调用TabLayoutMediator.at…

域内的三种委派方式

域委派&#xff1a;使得上游服务能使用用户凭据访问下游服务&#xff0c;使得下游服务根据域用户判断权限&#xff0c;例如&#xff1a; web 用户 hack ---------------访问------------------> web 服务器 &#xff08; www-data 域服务账户运行&#xff09;-------------…

GEE云计算、多源遥感、高光谱遥感技术蓝碳储量估算;红树林植被指数计算及提取

大气温室气体浓度不断增加&#xff0c;导致气候变暖加剧&#xff0c;随之会引发一系列气象、生态和环境灾害。如何降低温室气体浓度和应对气候变化已成为全球关注的焦点。海洋是地球上最大的“碳库”,“蓝碳”即海洋活动以及海洋生物&#xff08;特别是红树林、盐沼和海草&…

module ‘django.db.models‘ has no attribute ‘FieldDoesNotExist‘

module ‘django.db.models’ has no attribute ‘FieldDoesNotExist’ xadmin报错 原因 django与xadmin版本不匹配。 django==3.2.7 xadmin-django==3.0.2解决方案 在xadmin/view/edit.py的388行改为 from django.core import exceptions if self.request_method ==

数据结构(哈希表(中)纯概念版)

前言 哈希表&#xff08;Hash Table&#xff09;是计算机科学中的一个基础而重要的数据结构&#xff0c;它广泛评估各种算法和系统中&#xff0c;尤其是在需要快速查找、插入和删除操作的场景中。由于其O( 1)的平均时间复杂度&#xff0c;存储表在性能要求较高的应用中表现得非…