线性表(二)

∥☆過路亽.° 提交于 2020-01-27 21:58:49

单链表

线性表链式存储结构的特点是:用一种任意的存储单元存储线性表的存储元素(这组存储单元可以是连续的,也可以是不连续的)。因此,为了表示每个数据元素ai与其直接后继数据元素a i+1之间的逻辑关系,对数据元素来说,除了存储其本身的信息之外,还需存储一个指示其直接后续的信息(即直接后续的存储位置)。这两部分信息组成数据元素ai的存储映像,称为结点。它包括两个域:其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域。指针域中存储的信息称为指针或链。n个结点(ai(1<=i<=n)的存储印象)链结成一个链表,即为线性表

                 (a1,a2,a3..................an)

的链式存储结构。又由于此链表的每一个结点中只包含一个指针域,故又称线性链表单链表。

根据链表结点所含的指针个数、指针指向和指针连接方式,可将链表分为单链表、****循环链表双向链表用于实现线性表的链式存储结构,其他形式多用于实现树和图等非线性结构。

下面对首原结点、头结点、头指针三个容易混淆的概念加以说明

  • 首原结点是指链表中存储第一个数据元素a1的结点。
  • 头结点是在首元结点之前附设的一个结点,其指针域指向首元结点。头结点的数据域可以不存储任何信息,也可存储与数据元素类型相同的其他附加信息。
  • 头指针是指向链表中第一个结点的指针。若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点。

链表增加头结构的作用如下。
(1)便于首元结点的处理。
(2)便于空表和非空表的统一处理。

单链表基本操作的实现
1初始化
算法2.6 单列表的初始化
【算法步骤】
(1)生成新结点作为头结点,用头指针L指向头结点。
(2)头结点的指针域置空。
【算法描述】

Status InitList(LinkList &L)
{//构造一个空的单链表L
   L=new LNode;//生成新节点作为头结点,用头指针L指向头结点
   L->next=NUll;//头结点的指针域置空
   return OK;
   }

2 取值
和顺序表不同,链表中逻辑相邻的结点并没有存储在物理相邻的单元中,这样,根据给定的结点位置序号i,在链表中获取该结点的值不能像顺序表那样随机访问,而只能从链表的首元结点出发,顺着链域next逐个结点向下访问。
算法2.7单链表的取值
【算法步骤】
(1)用指针p指向首元结点,用j做计数器初始值赋为1.
(2)从首元结点开始依次顺着链域next向下访问,只要指向当前节点的指针p不为空,并且没有到达序号为i的节点,则循环执行如下操作在;

  • p指向下一个结点;
  • 计数器J相应加1.
  • (3)退出循环时,如果指针p为空,或者计数器j大于i,说明指定的序号i值不合法(i大于表长n或i小于等于0),取值失败返回ERROR;否则取值成功,此时j=i时,p所指的结点就是要找的第i个节点,用参数e保存当前结点的数据域,返回OK。
  • 【算法叙述】
- Status GetElem(LinkList L,int i,ElemType &e)
 - {//在带头结点的单列表L中根据序号i获取元素的值,用e返回L中第i个数据元素的值
 - p=L->next;j=1;//初始化,p指向首元结点,计数器j初值赋为1
 - while(p&&j<i)//顺链域向后扫描,直到p为空或p指向第i个元素
 - {
 -  p=p->next;//p指向下一个结点
 - ++j;//计数器j相应加1
 - }
 - if(!p||j>i)return ERROR;//i值不合法i>n或i<=0
 - e=p->data;//取第i个结点的数据域
 - return OK;
 - } 

3 查找
链表中按值查找的过程和顺序表类似,从链表的首元结点出发,依次将结点值和给定值e进行比较,返回查找结果。
算法2.8单链表的按值查找
【算法步骤】
①用指针p指向首元结点。
②从首元结点开始依次顺着链域next向下查找,只要指向当前结点的指针p不为空,并且p所指结点的数据域不等于给定值e,则循环执行以下操作: p指向下一个结点。
③返回p。若查找成功,P此时即为结点的地址值,若查找失败,P的值即为NULL。
【算法描述】

LNode *LocateElem (LinkList L, ElemType e)
{//在带头结点的单链表L中查找值为e的元素
p=L->next;  //初始化,P指向首元结点
while(p && p->data!=e)//顺链域向后扫描,直到p为空或p所指结点的数据域等于e
    p=p->next;//p指向下一个结点
    return p;
    }

[算法分析]
该算法的执行时间与待查找的值e相关,其平均时间复杂度分析类似于算法2.7,也为0(n)。
4插入
假设要在单链表的两个数据元素a和b之间插人一个数据元素x,已知P为其单链表存储结构中指向结点a的指针,如图2.11 (a)所示。
在这里插入图片描述
为插人数据元素x,首先要生成一个数据域为x的结点,然后插人到单链表中。根据插入操作的逻辑定义,还需要修改结点a中的指针域,令其指向结点x,而结点x中的指针域应指向结点b,从而实现3个元素a、b和x之间逻辑关系的变化。插人后的单链表如图2.11 (b)所示。假设s为指向结点x的指针,则上述指针修改用语句描述即为

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

算法2.9单链表的插入
【算法步骤】
将值为e的新结点插人到表的第1个结点的位置上,即插人到结点ai-1与ai之间,具体插人过程如图2.12所示,图中对应的5个步骤说明如下。
①查找结点ai-1并由指针p指向该结点。
②生成一个新结点s。
③将新结点
s的数据域置为e。
④将新结点s的指针域指向结点ai
⑤将结点
p的指针城指向新结点*s。
【算法描述】

Status ListInsert (LinkList GL,Int 1,ElemType e)
{//在带头结点的单链表L中第1个位置插人值为e的新结点
p=L;j=0;
while(p&& (j<i-1))
{p=p->next;++j;}//查找第1-1个结点,p指向该结点
if(!p||j>i-1)  return ERROR;//i>n+1或者i<1
s=new  LNode;//生成新结点*s
s->data=e;//将结点*s的数据域置为e
s->next=p->next;//将结点*s的指针域指向结点ai
p->next=s;//将结点*p的指针域指向结点*s
return OK;
}

[算法分析]
单链表的插人操作虽然不需要像顺序表的插人操作那样需要移动元素,但平均时间复杂度仍为0(n)。这是因为,为了在第i个结点之前插人一个新结点,必须首先找到第i- 1个结点,其时间复杂度与算法2.7相同,为O(n)。

在这里插入图片描述

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!