线性表的链式表示——单链表

news/2024/11/7 20:54:05/

目录

      • 一、单链表的定义
      • 二、单链表上基本操作的实现
        • 1、采用头插法建立单链表
        • 2、采用尾插法建立单链表
        • 3、按序号查找结点值
        • 4、按值查找表结点
        • 5、插入结点操作
        • 6、删除结点操作
        • 7、求表长操作
      • 三、双链表、循环链表、静态链表

顺序表可以随时存取表中的任意一个元素,它的存储位置可以用一个简单直观的公式表示, 但插入和删除操作需要移动大量元素链式存储线性表时,不需要使用地址连续的存储单元,即不要求逻辑上相邻的元素在物理位置上也相邻,它 通过“链”建立起数据元素之间的逻辑关系,因此 插入和删除操作不需要移动元素,而只需修改指针,但也会 失去顺序表可随机存取的优点

一、单链表的定义

线性表的链式存储又称单链表,它是指通过一组任意的存储单元来存储线性表中的数据元素。为了建立数据元素之间的线性关系,对每个链表结点除存放元素自身的信息外还需要存放一个指向其后继的指针。单链表节点结构如下图所示,其中data为数据域,存放数据元素;next为指针域,存放其后继结点的地址。
单链表结点结构
单链表中节点类型的描述如下:

typedef struct LNode{ElemType data;struct LNode *next;
}LNode,*LinkList;

利用单链表可以解决顺序表需要大量连续存储单元的缺点,但单链表附加指针域,也存在浪费存储空间的缺点。由于单链表的元素离散地分布在存储空间中,所以单链表时非随机存取的存储结构,即不能直接找到表中某个特定的结点。查找某个铁定的结点时,需要从表头开始遍历,依次查找

通常用头指针来表示一个单链表,如单链表L,头指针为NULL时表示一个空表。此外,为了操作上的方便,在单链表第一个结点之前附加一个结点,称为头结点。头结点的数据域乐意不设任何信息,也可以记录表长等信息。头结点的指针域指向线性表的第一个元素结点。

二、单链表上基本操作的实现

1、采用头插法建立单链表

该方法从一个空表开始,生成新节点,并将读取到的数据存放到新结点的数据域中,然后将新结点插入到当前链表的表头,即头结点之后。
头插法建立单链表
头插法建立单链表的算法如下:

LinkList List_HeadInsert(LinkList &L){//头插法建立单链表LNode *s;int x;L=(LinkList)malloc(sizeof(LNode));//创建头结点L->next=NULL;//初始为空链表scanf("%d",&x);//输入要创建的结点的值while(x!=9999){//输出9999,停止建立单链表s=(Lnode*)malloc(sizeof(LNode));//创建新结点s->data=x;s->next-L->next;//将新结点插入表中,L为头指针L->next=s;scanf("%d",&x);}return L;
}

采用头插法建立单链表时,读入数据的顺序与生成的链表中的元素的元素是相反的。每个结点插入的时间为O(1),设单链表长为n,则总时间复杂度为O(n)

2、采用尾插法建立单链表

头插法建立单链表的算法虽然简单,但生成的链表中结点的次序和输入数据的顺序不一致。若希望两者次序一致,则可采用尾插法。该方法将新结点插入到当前链表的表尾,为此必须增加一个尾指针r,使其始终指向当前链表的尾结点。
尾插法建立单链表

尾插法建立单链表的算法如下:

LinkList List_TailInsert(LinkList &L){//尾插法建立单链表int x;L=(LinkList)malloc(sizeof(LNode));//创建头结点LNode *s,*r=L;//r为表尾指针scanf("%d",&x);//输入结点的值while(x!=9999){//输入9999停止插入s=(LNode*)malloc(sizeof(LNode));s->data=x;r->next=s;//在表尾插入新结点r=s;//r指向新结点(使r始终指向链表表尾)scanf("%d",&s);}//插入结束r->next=NULL;//尾结点指针置空return L;
}

简单易得,尾插法的时间复杂度和头插法相同,为O(n)

3、按序号查找结点值

在单链表中从第一个结点出发,顺指针next域逐个往下搜索,指导找到第i个结点为止,否则(没找到)返回最后一个结点指针域NULL。
按序号查找结点值的算法如下:

LNode *GetElem(LinkList L,int i){//按序号查找结点值int j=1;//计数,从1开始查找LNode *p=L->next;//p指向第一个结点if(i==0) return L;//若要查找第0个结点,返回头结点if(i<1) return NULL;//若i序号为无效值,返回NULLwhile(p&&j<i){//从第一个结点开始,知道找到第i个结点(j==i)p=p->next;j++;}return p;//返回第i个结点的指针,若i大于表厂,则返回NULL
}

按序号查找操作的时间复杂度为O(n)

4、按值查找表结点

从单链表的第一个结点开始,由前往后依次比较表中各结点数据域的值,若某节点数据域的值等于给定值e,则返回该结点的指针;若整个单链表中没有这样的结点,则返回NULL。
按值查找表结点的算法如下:

LNode *LocateElem(LinkList L,ElemType e){LNode *p=L->next;while(p!=NULL&&p->data!=e) p=p->next;//从第一个结点开始查找data域为e的结点return p;//找到后返回该结点指针,否则返回NULL
}

按值查找操作的时间复杂度为O(n)

5、插入结点操作

插入节点操作将值为x的新结点插入到单链表的第i个位置上。先检查插入位置的合法性,然后找到待插入位置的前驱结点,即第i-1个结点,再在其后插入新结点。

算法首先调用按序号查找算法GetElem(L,i-1),查找第i-1个结点。假设返回的第i-1个结点为p,然后令新结点s的指针域指向p的后继结点,再令结点p的指针域指向新插入的结点*s。其操作过程如下图所示:
插入操作

实现插入结点的代码片段如下:

p=GetElem(L,i-1);//查找插入位置的前驱结点
s->next=p->next;
p->next=s;

注意:算法中第二、三条语句的顺序不能颠倒,否则,链表断链,无法找到插入结点的后继结点。
该算法主要的时间开销在于查找第i-1个元素时间复杂度为O(n)

6、删除结点操作

删除结点操作是将单链表的第i个结点删除。先检查删除位置的合法性,后查找表中第i–1个结点,即被删除结点的前驱结点,再将其删除。其操作过程如下图所示:
删除操作
假设结点p为找到的被删结点的前驱结点,为实现这一操作的逻辑关系的变化,仅需修改p的指针域,即将p的指针域next指向p的下一个结点。实现删除结点的代码片段如下:

p=GetElem(L,i-1);//查找删除位置的前驱结点
p=q->next;//令q指向被删除结点
p->next=q->next;//将*q结点从链中断开
free(q);//释放结点的存储空间

该算法的主要时间耗费在查找操作上时间复杂度为O(n)

7、求表长操作

求表长操作就是计算单链表中数据结点(不含头结点)的个数,需要从第一个结点开始顺序依次访问表中的每个结点。设置一个计数器变量,每访问一个结点,计数器加1,直到访问到空结点为止。算法的时间复杂度为O(n)
注意:单链表的长度不包括头结点。

三、双链表、循环链表、静态链表

另有链表的其他形式讲解见文章:【待插入】

本文为个人学习总结所得,如有错误和问题欢迎指正。


http://www.ppmy.cn/news/99897.html

相关文章

供收藏:国内各种免费可用ChatGPT实测(兼验伪) 版本不断更新补充 更新日期:2023/05/28

文章目录 供收藏&#xff1a;国内各种免费可用ChatGPT实测(兼验伪) 版本不断更新补充 更新日期&#xff1a;2023/05/28国内大厂的人工智能语言模型国内可访问的ChatGPT资源&#xff08;创业公司&#xff09;ZelinAI&#xff08;国内可直接访问的ChatGPT&#xff09;注册邀请码网…

JS 深度克隆的实现方法

方法一&#xff1a;正统做法&#xff08;扩展性高&#xff0c;推荐&#xff09; function test() { this.a 1; this.b 2; } test.prototype.c 3; // 原型上的属性 const obj new test(); console.log("原对象", obj); console.log("克隆后的对象", dee…

C# | 凸包算法之Andrew‘s,获取围绕一组点的凸多边形的轮廓点

C#实现凸包算法之Andrew’s 文章目录 C#实现凸包算法之Andrews前言示例代码实现思路测试结果结束语 前言 这篇关于凸包算法的文章&#xff0c;本文使用C#和Andrew’s算法来实现凸包算法。 首先消除两个最基本的问题&#xff1a; 什么是凸包呢&#xff1f; 凸包是一个包围一组…

个人博客-SpringBoot+Vue3项目实战(6)- 二次封装Axios

目录 前言新建axiosUtil.js 文件基本配置统一URL.env文件与环境变量示例参考资料 请求头超时时间 request 拦截器response 拦截器统一Api管理测试 前言 在上文中&#xff0c;我们封装了统一的后端数据返回结果&#xff0c;有了标准化的接口数据&#xff0c;我们就可以针对它&a…

[golang 微服务] 2. RPC架构介绍以及通过RPC实现微服务

一.简介 在上一节简单了解了微服务定义和优缺点之后&#xff0c;在使用微服务框架之前&#xff0c;需要首先了解一下RPC架构,通过RPC可以更形象了解微服务的工作流程 RPC的概念 RPC(Remote Procedure Call Protocol)&#xff0c;是 远程过程调用的缩写&#xff0c;通俗的说就是…

复合查询.

基本查询 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为大写的J select * from EMP where (sal>500 or jobMANAGER) and ename like J%;按照部门号升序而雇员的工资降序排序 select * from EMP order by deptno, sal desc;使用年薪进…

SpringBoot AOP切面编程 使用案例

参考资料 Springboot AOP实现指定敏感字段数据加密 &#xff08;数据加密篇 二&#xff09;【SpringBoot-3】切面AOP实现权限校验&#xff1a;实例演示与注解全解【小家Spring】Spring AOP中Pointcut切入点表达式最全面使用介绍AOP编程过程中的Signature接口 本篇文章核心思想…

C919用了哪些人工智能(AI)技术?

#国产大飞机C919商业首飞#近日&#xff0c;C919在国人的期盼下终于迎来了首次商飞&#xff0c;机票已公开售卖。众所周知&#xff0c;C919是一款全新的、先进的大飞机&#xff0c;那你知道它采用了哪些新的人工智能&#xff08;AI&#xff09;技术吗&#xff1f;下面让我来为大…