【实现类】

单链表的基本思想就是用指针表示结点之间的逻辑关系,因此要正确的对指针变量、指针、指针所指结点、结点的值进行区分。

设 p 是一个指针变量,则 p 的值是一个指针,若指针 p 指向某个 Node 类型的结点,则该结点用 *p 来表示,并称 *p 为结点变量,有时为了叙述方便,常将 " 指针变量 " 称为 " 指针 ",将 " 指针 p 所指的结点 " 称为 " 结点 p "。

在单链表中,结点 p 由两个域组成,其中,存放数据元素的部分用 p->data 表示,其值是一个数据元素;存放后继结点地址的指针部分用 p->next 表示,其值是一个指针。

template <class T>
struct Node{//结点T data;//数据域Node *next;//指针域
};template <class T>
class linkList{
private:Node<T> *first;//头指针
public:linkList();//无参构造函数linkList(T a[],int n);//有参构造函数~linkList();//析构函数int getLength();//获取单链表的长度T getElement(int i);//获取单链表的第i个元素int getPosition(T x);//获取单链表中值为x的元素序号void insertElement(int i,T x);//在单链表中第i个位置插入值为x的元素T deleteElement(int i);//删除单链表的第i的元素void print();//按序号依次输出单链表各元素
};

【构造函数】

1.带头结点的构造函数

1)无参构造函数

带头结点的无参构造函数,只需要生成一个头结点,让头指针指向头结点,并将头结点的指针域置空。

template <class T>
linkList<T>::linkList(){//无参构造函数first=new Node<T>;//头指针指向头结点first->next=NULL;//头结点的指针域置为空
}

2)头插法的有参构造函数

头插法就是每次将新申请的结点插到头结点的后面,即始终让新结点在第一的位置。

使用头插法建立有参构造函数,首先应建立一个头结点,让头指针指向头结点,同时将头结点的指针域置空(与无参构造函数相同),此后,对于 n 个数组元素:

  • 建立一个结点,为结点的数据域赋值
  • 将新结点的指针域指向头结点的指针域所指向的地址
  • 令头结点的指针域指向新结点

template <class T>
linkList<T>::linkList(T a[],int n){//头插法的有参构造函数first=new Node<T>;first->next=NULL;for(int i=0;i<n;i++){Node<T> *s=new Node<T>;//建立一个新结点s->data=a[i];//为新结点的数据域赋值s->next=first->next;//将新结点的指针域指向头结点的指针域所指向的位置first->next=s;//令头结点的指针域指向新结点}
}

3)尾插法的有参构造函数

尾插法就是每次将新申请的结点插到终端结点的后面,即始终让新结点在最后的位置。

使用尾插法建立有参构造函数,首先应建立一个头结点,然后再建立一个尾指针,代表单链表中最后一个数据元素的位置。此后,对于 n 个数据元素:

  • 建立一个结点,为结点的数据域赋值
  • 将尾指针指针域指向新建立的结点
  • 更改尾指针地址为新结点地址

最后,当单链表建立完毕时,将尾指针的指针域置空

template <class T>
linkList<T>::linkList(T a[],int n){//尾插法的有参构造函数first=new Node<T>;Node<T> *r=first;//尾指针初始化for(int i=0;i<n;i++){Node<T> *s=new Node<T>;//建立一个新结点s->data=a[i];为新结点数据域赋值r->next=s;//尾指针指针域指向新结点r=s;//更改尾指针地址为新结点地址}r->next=NULL;//单链表建立完毕,将尾指针指针域置空
}

2.不带头结点的构造函数

1)无参构造函数

不带头结点的无参构造函数,只需让头指针指向为空。

template <class T>
linkList<T>::linkList(){//无参构造函数first=NULL;//头指针置为空
}

2)头插法的有参构造函数

头插法就是每次将新申请的结点插到头结点的后面,即始终让新结点在第一的位置。

使用头插法建立有参构造函数,首先令头指针置为空(与无参构造函数相同),此后,对于 n 个数组元素:

  • 建立一个结点,为结点的数据域赋值
  • 将新结点的指针域指向头指针所指向的地址
  • 更改头指针地址为新结点地址
template <class T>
linkList<T>::linkList(T a[],int n){//头插法的有参构造函数first=NULL;for(int i=0;i<n;i++){Node<T> *s=new Node<T>;//建立一个新结点s->data=a[i];//为新结点的数据域赋值s->next=first;//将新结点的指针域指向头指针所指向的地址first=s;//更改头指针地址为新结点地址}
}

3)尾插法的有参构造函数

尾插法就是每次将新申请的结点插到终端结点的后面,即始终让新结点在最后的位置。

使用尾插法建立有参构造函数,首先令头指针置为空(与无参构造函数相同),再建立一个尾指针,代表单链表中最后一个数据元素的位置,然后建立一个初始结点,将其作为头结点:

  • 初始结点的数据域赋值为第 1 个数据元素的值
  • 初始结点的指针域指向头指针指向的地址
  • 更改头指针地址为初始结点地址
  • 更改尾指针地址为初始结点地址。

此后,对于 n 个数据元素:

  • 建立一个结点,为结点的数据域赋值
  • 将尾指针指针域指向新建立的结点
  • 更改尾指针地址为新结点地址
template <class T>
linkList<T>::linkList(T a[],int n){//尾插法的有参构造函数first=null;Node<T> *r=first;//尾指针初始化Node<T> *s=new Node<T>;//建立一个初始结点s->data=a[0];//为初始结点数据域赋值s->next=first;//初始结点指针域指向头指针的地址first=s;/更改头指针地址为初始结点地址r=first;//更改尾指针地址为新结点地址for(int i=1;i<n;i++){s=new Node<T>;//建立一个新结点s->data=a[i];为新结点数据域赋值r->next=s;//尾指针指针域指向新结点r=s;//更改尾指针地址为新结点地址}
}

3.带头结点和不带头结点的单链表的比较

操作 带头结点 不带头结点
空表 first=new node<T>;
first->next=NULL;
first=NULL
在空表中插入一个结点 s

Node<T> *s=new node<T>;

s->next=first->next;
first->next=s

Node<T> *s=new node<T>;

first=s;

插入一个结点,使其成为首元结点

Node<T> *s=new node<T>;

s->next=first->next;
first->next=s

Node<T> *s=new node<T>;

s->next=first;
first=s;

在某一结点 p 后插入结点 s

Node<T> *s=new node<T>;

s->next=p->next;
p->next=s

Node<T> *s=new node<T>;

s->next=p->next;
p->next=s;

【析构函数】

单链表中的结点是用 new 申请的,在释放单链表的对象时无法自动释放这些结点的存储空间,因此析构函数应将单链表中的结点的存储空间释放

template<class T>
linkList<T>::~linkList(){while(first!=NULL){Node<T> *q=first;//暂存要被释放的结点first=first->next;//first指向要被释放的结点的下一个结点delete q;//删除结点}
}

【获取线性表长度】

求单链表的长度时,在将单链表扫描同时建立一个累加器,直到扫描到链表尾,时间复杂度为 O(n)

template<class T>
int linkList<T>::getLength(){int count=0;//累加器Node<T> p=first->next;//工作指针while(p!=NULL){//p不为空p=p->next;//指向下一结点count++;//累加器+1}return count;
}

【查找元素】

1.按位查找

按位查找时,不能像顺序表按序号访问,只能从头指针出发顺着 next 域逐个结点向下搜索

  • 工作指针 p、位置累加器初始化
  • 直到 p 为空或指向第 i 个节点:工作指针后移、位置累加器+1
  • 若 p 为空,则第 i 个元素不存在,抛出位置异常;否则查找成功,返回节点 p 的数据元素
template<class T>
T linkList<T>::getElement(int i){int pos=1;//位置累加器Node<T> p=first->next;//工作指针while(p!=NULL&&pos<i){//p不为空且位置小于ip=p->next;//指向下一结点pos++;//累加器+1}if(p==NULL)throw "查找位置异常";elsereturn p->data;//返回数据元素
}

2.按值查找

按值查找时,需要对单链表中的元素依次进行比较,时间复杂度为 O(n)

  • 工作指针 p、位置累加器初始化
  • 直到 p 为空:工作指针后移、位置累加器+1
  • 若在循环中找到元素,则返回序号,即位置累加器的值;若退出循环,则查找失败,单链表中没有相关值
template<class T>
int linkList<T>::getPosition(T x){int pos=1;//位置累加器Node<T> p=first->next;//工作指针while(p!=NULL){//p不为空if(p->data==x)//找到值return pos;//返回位置p=p->next;//指向下一结点pos++;//累加器+1}return 0;//查找失败
}

【插入操作】

1.带头结点的插入

在单链表中的第 i 个位置插入元素,需要先找到第 i-1 个元素,若找不到则抛出位置异常,若找到则将新结点插入位置 i

  • 工作指针 p 、位置计数器初始化
  • 查找第 i-1 个节点,并使工作指针 p 指向该节点
  • 若查找不成功(P==NULL),说明位置错误,抛出位置异常,否则生成一个元素值为 x 的新节点 s,并将 s 插入到 p 之后
template<class T>
void linkList<T>::insertElement(int i,T x){int pos=0;//位置累加器Node<T> *p=first->next;//工作指针while(p!=NULL&&pos<i-1){//p不为空且位置小于i-1p=p->next;//指向下一结点pos++;//累加器+1}if(p==NULL)throw "插入位置异常";else{Node<T> *s=new Node<T>;//新申请一个结点s->data=x;//新结点数据域赋值为xs->next=p->next;//新结点s指向工作结点p之后的结点p->next=s;//工作结点p指向新结点s}
}

2.不带头结点的插入

不带头结点的插入与带头结点的插入十分相似,区别仅在于当插入位置是第一个位置时

template<class T>
void linkList<T>::insertElement(int i,T x){if(i==1){//若在第一个位置插入Node<T> *s=new Node<T>;//新申请一个结点s->next=first;//新结点的指针域指向头指针所指向的结点first=s;//令头指针指向新结点return;}int pos=1;//位置累加器Node<T> *p=first;//工作指针while(p!=NULL&&pos<i-1){//p不为空且位置小于i-1p=p->next;//指向下一结点pos++;//累加器+1}if(p==NULL)throw "插入位置异常";else{Node<T> *s=new Node<T>;//新申请一个结点s->data=x;//新结点数据域赋值为xs->next=p->next;//新结点s指向工作结点p之后的结点p->next=s;//工作结点p指向新结点s}
}

【删除操作】

在单链表中删除第 i 个位置的元素,需要先找到第 i-1 个元素,若找不到则抛出位置异常,若找到将第 i-1 个结点指针域所指向的结点删除

  • 工作指针 p 、位置计数器初始化
  • 查找第 i-1 个节点,并使工作指针 p 指向该节点
  • 若查找不成功(P==NULL),说明位置错误,抛出位置异常,否则将被删结点从链表中摘下并释放存储空间

template<class T>
T linkList<T>::deleteElement(int i){int pos=0;//位置累加器Node<T> *p=first;//工作指针while(p!=NULL&&pos<i-1){//p不为空且位置小于i-1p=p->next;//指向下一结点pos++;//累加器+1}if(p==NULL||p->next!=NULL)//p不存在或p的后继结点不存在throw "删除位置异常";else{Node<T> *s=p->next;//暂存被删结点T x=p->data;p->next=s->next;//摘链delete s;//释放空间return x;}
}

【遍历输出】

遍历输出即按照单链表的指针指向依次输出线性表各元素

  • 建立一个临时结点,令临时结点地址为头指针指向的结点
  • 当临时结点不为空时,输出数据域,并指向下一结点
template <class T>
void LinkList<T>:: print(){//依次输出线性表各元素Node<T> *p;//新建一个临时结点p=first->next;//令临时结点地址为头指针所指向的结点while(p!=NULL){//当临时结点不为空时cout<<(p->data)<<endl;//输出数据域p=p->next;//指向下一结点}
}

理论基础 —— 线性表 —— 单链表相关推荐

  1. python的线性链表_Python线性表——单链表-阿里云开发者社区

    Python线性表--单链表 线性表简介 线性表是一种线性结构,它是由零个或多个数据元素构成的有限序列.线性表的特征是在一个序列中,除了头尾元素,每个元素都有且只有一个直接前驱,有且只有一个直接后继, ...

  2. 数据结构_Java_基于 线性表-单链表的初始化、逆序、去重、非递减序列的合并(开辟新链表先整体插入一个链表全部元素,再遍历另外一个链表寻找合适位置插入 、开辟新链表实现舍弃原链表)等操作实现

    写在前面 不久前学习了数据结构线性表-数组-链表的相关知识,用C/C++语言实现了 单链表的系列相关操作 .见往期博客: 数据结构实验2_C语言_基于顺序表的非递减有序表的合并.线性表元素的增.删.改 ...

  3. 第二章:2线性表---单链表表示和实现

    前言: 为避免在使用线性表顺序存储结构的时,需插入和删除需大量移动元素的弊端. 本节讨论线性表的另外一种表示方法---链式存储结构: 由于它不要求逻辑上相邻的元素在物理位置上相邻,因此它对元素的插入和 ...

  4. 线性表(单链表)实验

    一.实验目的 1.线性表(LINE)的概念:数据元素之间存在着线性关系. 2.线性表的顺序表示和实现. 3.线性表的链式表示和实现. 4.线性表的基本操作:初始化.插入.修改.删除.遍历. 二.实验内 ...

  5. 2.2线性表——单链表基本操作的实现

    注意:以下内容均省略思路,只有代码和时间复杂度.此内容为本人学习过程中的一些学习记录,如有错误,恳请各位指正.建议,末学将感激不尽! 目录 用结构指针描述单链表 初始化链表 判断空表 销毁单链表 清空 ...

  6. 线性表----单链表

    一,实验内容:链表的创建.插入与删除操作 二.程序清单 三.思考 l.如果需要将新结点 插入  到     第i个数据元素之后,算法将如何改动? 2. 双向链表和循环链表的定义和构造方法. //链表的 ...

  7. 线性单链表存储结构c语言代码,单链表定义-(线性表的链表存储结构)

    线性表分为:顺序存储结构和连存储结构 顺序存储结构的优点: 1.空间利用率高,几乎不需要额外的空间开销. 2.数据的逻辑结构和物理结构完全一致. 3.结点地址计算的时间和线性表的规模大小无关. 4.可 ...

  8. 头歌实践教学平台数据结构与算法:02线性表——反转链表(拓展)

    针对数据结构02线性表--反转链表(拓展)在头歌平台练习过程中的完成代码,每题思路单独在每一关中解释.如有其他需求请留言. 第一关 已知一个带头结点的单链表L,将L反转并返回反转后的单链表.要求将L的 ...

  9. Algorithms_基础数据结构(04)_线性表之链表_单向循环链表约瑟夫环问题

    文章目录 大纲图 链表的经典面试题目 如何设计一个LRU缓存淘汰算法 约瑟夫问题 结构 分析 大纲图 链表的经典面试题目 如何设计一个LRU缓存淘汰算法 tip:单向链表 约瑟夫问题 N个人围成一圈, ...

最新文章

  1. 推荐系统笔记:无任何限制的矩阵分解
  2. 创建安全 Windows CE 设备(转)
  3. 四边形可以分为几类_四边形有几种类型
  4. QT Creator应用程序开发——信号与槽
  5. session 学习
  6. Mac安装brew及报错处理办法
  7. 蓝色大巴汽车网站404页面源码
  8. 你要了解的11款面向Linux系统的一流备份实用工具
  9. 4.12_proxy_结构型模式:代理模式
  10. 踢爆关于创业公司的五大认知误区!
  11. BP神经网络——从二次代价函数(Quadratic cost)到交叉熵(cross-entropy cost)代价函数
  12. apan在PHP什么意思,Apanteles是什么意思
  13. JavaScript入门之Canvas(一): 2D Context
  14. 第一百天 how can i 坚持
  15. lg手机历史机型_细数LG G系列旗舰手机进化史
  16. Java程序员面试分类真题(后附答案解析)
  17. 【剖析 | SOFARPC 框架】之SOFARPC 连接管理与心跳剖析
  18. 沈阳市计算机学校1996届,生命科学学院1996届应用生物班校友回母校举行毕业20周年联谊会...
  19. EPLAN 设备选择
  20. ubuntu如何查看当前的ROS发行版本

热门文章

  1. 手把手教你用ECharts画折线图
  2. java无限循环可变参数,Java可变参数、加强for循环
  3. 推流工具_【软件分享】小熊录屏VIP版(手机直播游戏必备推流工具)
  4. MySQL学习笔记_2_MySQL创建数据表(上)
  5. java 中equals和==的区别
  6. iOS开发UI篇—核心动画(基础动画)
  7. Redis命令——键(key)
  8. Java8 新特性 Optional
  9. MySQL数据库(六) 一一 基本操作之事物和索引
  10. 正则表达式与扩展正则表达式区别