第二章 线性表的链式表示和实现

一、链表的有关术语

(一)链式存储结构

  1. ** 链式存储结构**:结点在存储器中的位置是任意的即逻辑上相邻的数据元素在物理上不一定相邻。 线性表的链式表示又称为非顺序映像链式映像。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(二) 与链式存储有关的术语

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(三)链表(链式存储结构)的特点

  • 结点在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理上不一定相邻。
  • 访问时只能通过头指针进入链表,并通过每个结点的指针域向后扫描其余结点,所以寻找第一个结点和最后一个结点所花费的时间不等。
  • 这种存取元素的方法被称为顺序存取法。

链表的优缺点
优点
①数据元素的个数可以自由扩充。
②插入、删除等操作不必移动数据,只需修改链接指针,修改效率较高。
缺点
①存储密度小
②存取效率不高,必须采用顺序存取,即存取数据元素时,只能按链表的顺序进行访问(顺藤摸瓜)
在这里插入图片描述

二、单链表的定义和表示

在这里插入图片描述

  • 单链表的存储结构定义
typedef struct LNode
{
	ElemType data;  //数据域
	struct LNode *next;  //指针域,其类型为指向节点的指针类型LNode*
}LNode,*LinkList; //*LinkList为指向结构体LNode的指针类型

在这里插入图片描述
在这里插入图片描述

三、单链表基本操作的实现

在这里插入图片描述

(一)初始化

单链表的初始化操作就是构造一个空表。

  1. 单链表的初始化
  • 算法步骤在这里插入图片描述
  • 算法描述
Status InitList_L(LinkList &L){
//构造一个空的单链表L
	L=new LNode;  //生成新节点作为头结点,用头指针L指向头结点
	L->next=NULL; //头指针的指针域置空
	return OK;
}

(二)取值

在这里插入图片描述

根据位置i获取相应位置数据元素的内容,只能从链表的首元节点出发,顺着链域next逐个节点向下访问。

  1. 单链表的取值
  • 算法步骤

在这里插入图片描述

①用指针p指向首元节点,用j做计数器初值赋1
②从首元节点开始依次顺着链域next向下访问,只要指向当前节点的指针p不为空(NULL),并且没有到达序号为i的节点,则循环执行以下操作:

  • p指向下一个节点;
  • 计数器j相应加1

③退出循环时,如果指针p为空,或者计数器j大于i,说明指定的序号i值不合法(i大于表长n或小于等于0);取值失败返回ERROR;否则取值成功,此时j=i时,p所指的节点就是要找的第i个节点,用参数e保存当前节点的数据域,返回OK。

  • 算法描述
//获取线性表L中的某个数据元素的内容
Status GetElem_L(LinkList L,int i,ElemType &e){
//在带头结点的单链表L中根据序号i获取元素的值,用e返回L中第i个数据的值
	p=L->next;j=1;  //初始化,p指向首元节点,计数器j初值赋为1
	while(p&&j<i){  //向后扫描,直到p指向第i个元素或p为空
	p=p->next;  //p指向下一个节点
	++j;   //计算器相应加1
	}
	if(!p || j>i) return ERROR;  //第i个元素不存在
	e=p->data;  //取第i个元素
	return OK;
}//GetElem_L

算法的时间复杂度:T(n) = O(n)
平均查找长度:ASL = (n-1)/2

(三)查找

根据指定数据获取数据所在的位置,从链表的首元节点出发,依次将节点值和给定值e进行比较,返回查找结果。

  1. 单链表的按值查找
  • 算法步骤
    在这里插入图片描述

①用指针p指向首元节点
②从首元节点开始依次顺着链域next向下查找,只要指向当前节点的指针p不为空,并且所指节点的数据域不等于给定值e,则循环执行以下操作:p指向下一个节点
③返回p。若查找成功,p此时指向节点的地址值,若查找失败,则p的值为NULL。

  • 算法描述
//在线性表L中查找值为e的数据元素
LNode *LocateElem_L(LinkList L,ElemType e){
	p=L->next;  //初始化,p指向首元节点
	while(p && p->data != e)  //顺序链域向后查找,直到p为空或者所指节点的数据域等于e
		p=p->next;  //p指向下一个节点
	return p;  //查找成功返回值为e的节点地址p,查找失败p为NULL

算法的时间复杂度:T(n) = O(n)

(四)插入

  1. 单链表的插入

在这里插入图片描述

  • 算法步骤

在这里插入图片描述

  • 算法描述
//在L中的第i个元素之前插入数据元素e
Status ListInsert_L(LinkList &L,int i,ElemType e){
	p=L;j=0;
	while(p && (j<i-1)){
		p=p->next;++j; } //查找第i-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的指针域指向节点a
	p->next=s;   //将节点*p的指针域指向节点*s
	return OK;
}//ListInsert_L
	

算法的时间复杂度:T(n) = O(n)

(五)删除

  1. 单链表的删除

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 算法步骤
    在这里插入图片描述
  • 算法描述
//将线性表L中第i个数据元素删除
Status ListDelete_L(LinkList &L,int i){
//在带头结点的单链表L中,删除第i个元素
	p=L;j=0;
	while((p->next) && (j<i-1)){  //查找第i-1个节点,p指向该节点
		p=p->next;++j; } 
	if(!(p->next) || (j>i-1)) return ERROR;  //i>n或者i<1,删除位置不合理
	q=p->next;   //临时保存被删节点的地址以备释放
	p->next=q->next; //改变删除节点前驱节点的指针域
	delete q;  //释放删除节点的空间
	return OK;
}//ListDelete_L

算法的时间复杂度:T(n) = O(n)
在这里插入图片描述

(六)创建单链表

1.前插法

  • 算法步骤
    在这里插入图片描述
    在这里插入图片描述
  • 算法描述
void CreateList_H(LinkList &L,int n){
//逆位序输入n个元素的值,建立带表头节点的单链表L
	L=new LNode;  
	L->next=NULL;  //先建立一个带头节点的空链表
	for(i=0;i<n;++i){
		p=new LNode;  //生成新节点*p
		cin>>p->next;  //输入元素值赋给新节点*p的数据域
		p->next=L->next;  //将新节点*p插入到头结点之后
		L->next=p;
	}
}

算法的时间复杂度:T(n) = O(n)

2.后插法

  • 算法步骤
    在这里插入图片描述
  • 算法描述
void CreateList_R(LinkList &L,int n){
//正位序输入n个元素的值,建立带表头节点的单链表L
	L=new LNode;  
	L->next=NULL;  //先建立一个带头节点的空链表
	r=L;  //尾指针r指向头结点
	for(i=0;i<n;++i){
		p=new LNode;  //生成新节点
		cin>>p->data;  //输入元素值赋给新节点*p的数据域
		p->next=NULL;  //将新节点*p插入到尾结点*r之后
		r->next=p;
		r=p;  //r指向新的尾结点*p
	}
}

算法的时间复杂度:T(n) = O(n)

四、循环链表

(一)单循环链表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(二)循环链表的合并

在这里插入图片描述
实例代码:

LinkList Connect(LinkList Ta,LinkList Tb)
{ //假设Ta,Tb都是非空的单循环链表
	p=Ta->next;  //p存表头节点
	Ta->next=Tb->next->next; //Tb表头连接Ta表尾
	delete Tb->next;  //释放Tb表头结点
	Tb->next=p;  //修改指针
	return Tb;
}

五、双向链表

在这里插入图片描述
双向链表的存储结构:

typedef struct DuLNode{
	ElemType data;   //数据域
	struct DuLNode *prior;  //指向直接前驱
	struct DuLNode *next;  //指向直接后继
}DuLNode,*DuLinkList

在这里插入图片描述

(一)双向链表的插入

在这里插入图片描述

Status ListInsert_Dul(DulinkList &L,int i,ElemType e)
{  //在带头结点的双向链表L中第i个位置之前插入元素e

	if(!(p=GetElemP_Dul(L,i))) //在L中确定第i个元素的位置指针p
		return ERROR;   //p为NULL时,第i个元素不存在
	s=new DulNode;  //生成新节点*s
	s->data=e;   //将节点*s的数据域置为e
	s->prior=p->prior;  //将节点*s插入L中,对应上图的①
	p->prior->next=s;  //对应上图的②
	s->next=p;   //对应上图的③
	p->prior=s; //对应上图的④
	return OK;
}

(二)双向链表的删除

在这里插入图片描述

Status ListDelete_Dul(DulinkList &L,int i)
{  //删除带头结点的双向链表L中的第i个元素
	if(!(p=GetElemP_Dul(L,i))) //在L中确定第i个元素的位置指针p
		return ERROR;   //p为NULL时,第i个元素不存在
	p->prior->next=p->next;  //修改删除节点的前驱节点的后继指针
	p->next->prior=p->prior;  //修改被删节点的后继节点的前驱指针
	delete p;  //释放被删节点的空间
 	return Ok;
}
	

(六)顺序表和链表的比较

在这里插入图片描述

(七)线性表的应用

(一)线性表的合并

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 算法步骤
    ①分别获取La表长m和Lb表长n
    ②从Lb中第1个数据元素开始,循环n次执行以下操作:
    • 从Lb中查找第i(1≤i≤n)个元素数据赋给e
    • 在La中查找元素e,如果不存在,则将e插在表La的最后
  • 算法描述
void MergeList(List &La,List &Lb)
{  //将所有在线性表Lb中但不在La中的数据元素插入La中
	m=ListLength(La);  //求线性表的长度
	n=ListLength(Lb);   //求线性表的长度
	for(i=1;i<=n;i++){
		GetElem(Lb,i,e);  //取Lb中第i个数据元素赋给e
		if(!LocateElem(La,e))  //La不存在和e相同的数据元素
			ListInsert(&La,++m,e);  //将e插在La的最后
	}
}

算法的时间复杂度:T(n) = O(m*n)
在这里插入图片描述

(二)有序表的合并

在这里插入图片描述

1. 顺序有序表的合并

  • 算法步骤
    ①创建一个表长为m+n的空表LC
    ②指针pc初始化,指向LC的第一个元素
    ③指针pa和pb初始化,分别指向LA和LB的第一个元素
    ④当指针pa和pb均未达到相应表尾时,则依次比较pa和pb所指向的元素值,从LA或LB“摘取”元素值较小的节点插入LC的最后
    ⑤如果pb已到达LB的表尾,依次将LA的剩余元素插到LC的最后
    ⑥如果pa已到达LA的表尾,依次将LB的剩余元素插到LC的最后
  • 算法描述
void MergeList_Sq(SqList LA,SqList LB,SqList &LC)
{  //已知顺序有序表LA和LB的元素按值非递减排列
   //归并LA和LB得到新的顺序有序表LC,LC的元素也按值非递减排序
   LC.length=LA.length+LB.length;  //新表长度为待合并两表的长度之和
   LC.elem=new ElemType[LC.length];  //为合并后的新表分配一个数组空间
   pc=LC.elem;   //指针pc指向新表的第一个元素
   pa=LA.elem; 
   pb=LB.elem;  //指针pa和pb的初值分别指向两个表的第一个元素
   pa_last=LA.elem+LA.length-1;  //指针pa_last指向LA的最后一个元素
   pb_last=LB.elem+LB.length-1;  //指针pb_last指向LB的最后一个元素
   while((pa<=pa_last) && (pb<=pb_last)){  //为达到LA和LB的表尾
   		if(*pa<=*pb) *pc++=*pa++;  //依次摘取两表中值较小的节点插入LC的最后
   		else *pc++=*pb++; 
   	}
   	while(pa<=pa_last)  *pc++=*pa++;  //已到达LB表尾,依次将LA的剩余元素插入到LC的最后
   	while(pb<=pb_last)  *pc++=*pb++;  //已到达LA表尾,依次将LB的剩余元素插入到LC的最后
}

·

2. 链式有序表的合并(重点掌握)

在这里插入图片描述
在这里插入图片描述

  • 算法步骤
    在这里插入图片描述
    ①有序链表合并(初始化)
    在这里插入图片描述
    ②有序链表合并(pa->data<=pb->data)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    ③有序链表合并(pa->data>pb->data)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 算法描述
void MergeList_L(LinkList &LA,LinkList &LB,LinkList &LC){
//已知单链表LA和LB的元素按值非递减排列
//归并LA和LB得到新的单链表LC,LC的元素也按值非递减排列
	pa=LA->next;pb=LB->next;  //pa和pb的初值分别指向两个表单的第一个结点
	LC=LA;  //用LA的头结点作为LC的头节点
	pc=LC;  //pc的初值指向LC的头节点
	while(pa&&pb){
	//LA和LB均为到达表尾,依次摘取量表中值较小的节点插入到LC的最后
		if(pa->data<=pb->data){  //摘取pa所指节点
			pc->next=pa; //将pa所指节点链接到pc所指节点之后
			pc=pa;   //pc指向pa
			pa=pa->next;  //pa指向下一节点
		}
		else{   //摘取pb所指节点
			pc->next=pb; //将pb所指节点链接到pc所指节点之后
			pc=pb;   //pc指向pb
			pb=pb>next;  //pb指向下一节点
		}
	}     //while
	pc->next=pa?pa:pb;  //将非空表的剩余段插入到pc所指节点之后
	delete LB;   //释放LB的头节点
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(八)算法设计题

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值