《数据结构》C语言版(严蔚敏)——线性表(一)

一、线性表定义与特点

1.定义

由n个数据特性相同的元素构成的有限序列称为线性表
n为线性表的的长度,n=0时为空表

2.特点

非空线性表或线性结构

  1. 唯一“第一个”数据元素
  2. 唯一“最后一个”数据元素
  3. 除第一个外,每个数据元素都只有一个前驱
  4. 除最后一个,都只有一个后继

3.类型定义

(见具体程序练习)

二、线性表的顺序表示和实现

顺序表:用一组地址连续的存储单元依次存储线性表的数据元素;
特点:逻辑上相邻,物理次序上相邻
第i个元素的存储位置:LOC(ai )=LOC(a1)+(i-1)*l
(设每个元素占用l个存储单位)
优点

  • 存储密度大(结点本身所占存储量/结点结构所占存储量)
  • 可以随机存取表中任一元素
    缺点
  • 在插入、删除某一元素时,需要移动大量元素
  • 浪费存储空间
  • 属于静态存储形式,数据元素的个数不能自由扩充

顺序表的基本操作实现

初始化

Status	InitList(SqList	&L)
{//构造一个空的顺序表L
	L.elem=new	ElemType[MAXSIZE];	//为顺序表分配一个大小为MAXSIZE的数组空间 
	if(!L.elem)exit(OVERFLOW);		//存储分配失败退出 
	L.length=0;						//空表长度为0 
	return	OK; 
 } 

取值
根据指定的位置序号i,获取顺序表中第i个元素的值。
随即存储的特点,直接通过数组下标定位得到,elem[i-1]单元存储第i个元素

Status	GetElem(SqList	L,int	i,ElemType	&e)
{
	if(i<1||i>L.length)	return	ERROR;	//判断i值是否合理,不合理返回error 
	e=L.elem[i-1]//elem[i-1]单元存储第i个数据元素 
	return	OK; 
}

顺序表取值算法的时间复杂度为:O(1)
查找
根据指定的元素值e查找顺序表中第一个与e相等的元素

int	LocateElem(SqList	L,ElemType	e)
{//在顺序表L中查找值为e的数据元素,返回其序号
	for(i-0;i<L.length;i++)
		if(L.elem[i]==e )	return	i+1;	//查找成功,返回序号i+1 
	return	0;//查找失败返回0 
}

平均查找长度 ASL=(n+1)/2
平均时间复杂度:O(n)
插入
在表的第i个位置插入一个新的数据元素e,使长度为n的线性表变成长度为n+1的线性表,一般需从最后一个元素即第n个元素开始,依次向后移动一个位置,直至第i个(共n-i+1个)

Status	ListInsert(SqList	&L,ElemType	e)
{//在顺序表L中第i个位置插入新的元素e,i值的合法范围是1<=i<=L.length+1
	if(i<1)||(i>L.length+1)	return	ERROR;//i值不合法
	if(L.length==MAXSIZE)	retur	ERROR;//当前存储空间已满
	for(j=L.length-1;j>=i-1;j--)
	L.elem[j+1]=L.elem[j];//插入位置及之后的元素后移
	L.elem[i-1]=e;//将新元素e放入第i个位置
	++L.length;//表长加1
	return	OK;
}

平均次数Eins=n/2
平均时间复杂度: O(n)
删除
将表的第i个元删除,使长度为n的线性表变成长度为n-1的线性表,一般需将第i+1个至第n个(共n-i个)依次向前移动一个位置(i=n无需移动)

Status	ListDelete(SqList	&L,int	i)
{//在顺序表L中删除第i元素,i值的合法范围是1<=i<=L.length
	if(i<1)||(i>L.length)	return	ERROR;//i值不合法
	for(j=i;j《=L.length-1;j++)
	L.elem[j-1]=L.elem[j];//被删除元素之后的元素前移 
	--L.length;//表长减1
	return	OK;
}

平均次数Edel=(n-1)/2
平均时间复杂度: O(n)

三、线性表的链式表示和实现

1.单链表的定义和表示

单链表由表头指针唯一确定

typedef	struct	LNode
{
	ElemType	data;	//结点的数据域 
	struct	LNode	*next;//结点的指针域 
}LNode,*LinkList;//LinkList为指向结构体LNode的指针类型 

LinkList与LNode为同一结构体指针类型,本质等价
习惯用LinkList定义单链表,LNode
定义指向单链表中任意结点的指针变量
LinkList L/LNode *p 称该链表为表L,L为头指针命名

  • 首元结点:链表中存储的第一个数据元素a1的结点
  • 头结点:首元结点之前附设的一个结点,指针域指向首元结点
  • 头指针:指向链表中第一个结点的指针
    增加头结点作用
  • 便于首元结点的处理 首元结点的地址保存在头结点的指针域中,在链表的第一个位数据元素的操作和其它相同,无须进行特殊处理;
  • 便于空表和非空表的统一处理 不设头结点时当单链表的长度为零的空表,头指针为空(L==NULL)
    无论链表是否为空,头指针都是指向头结点的非空指针。
    若p->data=ai,则p->next->data=ai+1.单链表是非随机存取的存储结构,必须从头指针出发顺链进行寻找,称为顺序存取的存取结构。

2.单链表基本操作的实现

初始化
构造一个空表

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

取值
不同于顺序表,逻辑相邻的结点没有储存在物理相邻的单元中,不能像顺序表一样随机访问,只能从链表首元结点出发顺着链域next逐个结点向下访问

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>1)return	ERROR;//i值不合法i>n或i<=0
	 e=p->data;//取第i个结点的数据域 
	return	ok; 
 } 

ASL=(n-1)/2
平均时间复杂度: O(n)
查找
与顺序表类似,从首元结点出发以此比较

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的结点地址p,查找失败p为NULL 
 } 

与顺序表类似
平均时间复杂度: O(n)
插入
s->next=p->next;p->next=s;

Status	ListInsert(LinkList	&L,int	i,ElemType	e)
{//在带头结点的单链表L中第i个位置插入值为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的指针域指向结点ai
	p->next=s;//将结点*p的指针域指向结点*s
	return	OK;	
}

不需要像顺序表一样移动元素,但在第i个结点前插入元素,需先找到第i-1个结点
平均时间复杂度: O(n)
删除
p->next=p->next->next;在修改指针前,应引入另一指针q,临时保存b的地址以备释放

Status	ListDelete(LinkList	&L,int	i)
{//在带头结点的单链表L中删除第i个元素 
	p=L;j=0;
	while((p=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;	
}

平均时间复杂度: O(n)
创建单链表

  • 前插法请添加图片描述
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;L->next=p;//将新结点*p插入到头结点之后 
	} 
 } 

平均时间复杂度: O(n)

  • 后插法
    请添加图片描述
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;//生成新结点*p
		cin>>p->next;//输入元素值赋给新结点*p的数据域
		p->next=NULL;r->next=p;//将新结点*p插入到尾结点*r之后 
		r=p;//r指向新的尾结点*p 
	} 
 } 

平均时间复杂度: O(n)

3.循环链表

特点:表中最后一个结点指针域指向头结点,整个链表形成一个环;
与单链表的差别:单链表判别条件:p!=NULL或p->next!=NULL,循环单链表为p!=L或p->next!=L
请添加图片描述
两线性表合并为一个

p=B->next->next;
B->next=A->next;
A->next=p; 

时间复杂度: O(1)
请添加图片描述

4.双向链表

克服单链表的单向性
两个指针域

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

请添加图片描述
插入结点时需修改4个指针,删除结点时需修改2个指针
插入

Status	ListInsert_DuL(DuLinkList	&L,int	i,ElemType	e)
{//在带头结点的双向链表L中第i个位置之前插入元素e
	if(!(p=GetElem_DuL(L,i)))//在L中确定第i个元素的位置指针p
	return	ERROR;//p为NULL时,第i个元素不存在
	s=new	DuLNode;//生成新结点*s
	s->data=e;//将新结点*s数据域置为s
	s->prior=p->prior;//将新结点*s插入L中,对应图2.20.1
	p->prior->next=s;//对应2.20.2
	s->next=p;//对应2.20.3
	p->prior=s;//对应2.20.4
	return	OK; 
}

删除

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

二者时间复杂度均为O(n)
请添加图片描述

四、顺序表和链表的比较

请添加图片描述请添加图片描述

五、线性表的应用

1.线性表的合并

时间复杂度: O(m*n)

2.有序表的合并

  • 顺序有序表:
    时间复杂度: O(m+n)
    空间复杂度: O(m+n)
  • 链式有序表:
    时间复杂度: O(m+n)
    空间复杂度: O(1)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值