我是靠谱客的博主 可耐眼神,最近开发中收集的这篇文章主要介绍线性表,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一. 线性结构的基本特点

除第一个元素无直接前驱,最后一个元素无直接后继外,其他每个数据元素都有一个前驱和后继。

二. 线性表的特点和定义

1.由n(n>=0)个数据特性相同的元素构成的有限序列称为线性表

2.线性表中的个数n定义为线性表的长度,n=0时称为空表

3.对于非空线性表和线性结构的特点

存在唯一的一个被称作“第一个”的数据元素
存在唯一的一个被称作“最后一个”的数据元素
除第一个之外,结构中的每个数据元素均只有一个前驱
除最后一个之外,结构中的每个数据元素均只有一个后继

三. 线性表的顺序存储表示

1.用一组地址连续的存储单元依次存储线性表的数据元素,称这种存储结构的线性表为顺序表
特点是逻辑上相邻的数据元素,其物理次序也是相邻的。
2.假设线性表的每个元素需占用l个存储单元
LOC(a i+1)=LOC(a i)+ l
LOC(a i)=LOC(a 1)+(i-1)* l
3.线性表中的任意数据元素都可随机存取,所以线性表的顺序存储结构是一种随机存取的存储结构。
4.表示
常用数组来描述数据结构中的顺序存储结构

#define MAXSIZE 100//顺序表可能达到的最大长度
typedef struct
{
  type *elem;//存储空间的地址
  (或者type data[MAXSIZE])
  int length;//长度
}Sqlist

四.线性表的顺序存储的基本操作

1.初始化
构造一个空的顺序表

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

也可利用动态存储有效利用系统资源
2.取值
获取顺序表中第i个元素的值,算法时间复杂度为O(1)

Status Getelem(Sqlist L,int i,Elemtype &e)
{
 if(i<1||i>L.length)
 return ERROR;
 e=L.elem[i-1];
 return ok;
 }

3.查找
查找数值为e的元素返回其序号,平均时间复杂度为O(n)

int Locateelem(Sqlist L,elemtype e)
{
for(i=0;i<L.length;i++)//遍历数组
{
if(L.elem[i]==e)
return i+1;//找到后返回序号
}
return 0;//查找失败返回0

4.插入
在表的第i个位置插入一个新的数据元素e
将ai之后的数据(倒序)往后移一位,然后插入,表长加一
时间复杂度为O(n)

Status Listinsert(Sqlist &L,int i,Elemtype e)
{
if(i<1||i>L.length+1)//判断i是否合法
return ERROR;
if(i>MAXSIZE)
return ERROR;
for(j=L.length-1;j>=i-1;j--)//倒序
{
 L.elem[j+1]=L.elem[j];//将元素后移
}
L.elem[i-1]=e;//插入新值
++L.length;//表长加一
return ok;
}

5.删除
删去第i个元素,即将ai之后的元素依次前移
平均时间复杂度为O(n)

Status Listdelete(Sqlist &L,int i)
{
	if(i<1||i>L.length+1)
	return ERROR;//不合法输入
	for(j=i;j<L.length;j++)//正序
	{
		L.elem[j-1]=L.elem[j];//前移
	}
	L.length--;//表长减1
	return ok;
}

五. 线性表的链式表示

1.用一组任意的存储单元存储线性表的数据元素
!这组存储单元(物理位置)既可以是连续的也可以是不连续的

2.结点
信息+后继的存储位置
数据域+指针域
n个结点链接成一个链表

3.表示

typedef struct LNode
{
	elemtype data;//数据元素
	struct LNode *next;//后继指针
}NOde,*Linklist;

4.首元结点,头结点,头指针
首元结点是指链表中存储第一个元素a1的结点
头结点是在首元结点之前设的一个结点,其指针域指向首元结点,数据域可以不存储信息也可以存储长度
头指针是指向链表中第一个结点的指针(指向头结点或首元结点)
链表增加头结点的作用
*便于首元结点的处理,不需进行特殊处理
*便于空表和非空表的统一处理,头指针都是指向头结点的非空指针
单链表是顺序存取的存取结构

六.单链表基本操作

1.初始化
生成新结点,头指针指向头结点,头指针的指针域置空

Status Initlist(Linklist &L)
{
	L=new Lnode;
	L.next=NULL;
	return ok;
}

2.取值
从首元结点开始依次访问,取第i个值
(n-1)/2 平均时间复杂度为O(n)

Status Get(Linklist &L,int i,Elemtype &e)
{
	p=L->next;//指向首元结点
	j=1;//计数
	while(p&&j<i)//必须判断p非空
	{
	  p=p->next;
	  j++;
        }
        if(!p||j>i)//!p对应i>n,j>i对应输入i<0
        return error;
        e=p->data;//找到目标数据
        return ok;
}

3.按值查找
遍历,平均时间复杂度O(n)

Lnode *LocateElem(Linklist L,Elemtype e)
{
	p=L->next;
	while(p)
	{
	if(p->data!=e)
	p=p->next;
	else return p;
	}

}

4.插入
将值为e的新结点插入第i个结点的位置,先找到该位置的前驱
平均时间复杂度O(n)

Status Listinsert(Linklist &L,int i,Elemtype e)
{
	p=L;
	j=0;//如果是p=l->next,j=1需特殊处理i=1的情况
	while(p&&j<i-1)
	{
		j++;
		p=p->next;
	}//找到要插入的位置,此时p指向a(i-1)
	if(!p||(j>i-1))
	return error;
	s=new LNode;//创建新结点
	s->data=e;//为新结点赋值
        s->next=p->next;//将ai和新结点连接起来
	p->next=s;//将新结点链到a(i-1)后
	
}

5.删除
修改待删除结点前驱的指针域以及释放待删除结点所占空间
平均时间复杂度是O(n)

Status ListDelete(LinkList &l,int i)
{
	p=L;
	j=0;
	while((p->next)&&(j<i-1))//找待删除结点的前驱,所以是p->next
	{
		p=p->next;
		j++;
	}
	if((p->next==NULL)||(j>i-1))
	return error;
	q=p->next;//临时保存待删结点以备释放
	p->next=q->next;//待删结点前驱与后继链接
	delete q;//释放删除结点
	return ok;
}

注意:
删除操作中的循环条件和插入不一样,
删除while((p->next)&&(j<i-1))
插入while(p&&j<i-1)
插入操作可以出现被插入位置前驱是最后一个结点的情况,即插入到最后一个结点之后,i=n+1,而删除操作不会出现待删除结点前驱是最后一个结点的情况,因为没有这个结点。

6.创建单链表
A后插法创建单链表

将新结点插入链表的尾部,按输入顺序构建链表存储数据
时间复杂度O(n)

void Createlist_R(LinkList &L,int n)
{
	L=new LNode;//建立一个空链表
	L->next=NULL;
	r=L;//设置一个尾指针
	for(i=0;i<n;i++)
	{
		p=new LNode;//建立一个新结点
		cin>>p->data;//输入数据
		p->next=NULL;//尾
		r->next=p;//将p连到尾指针之后
		r=p;//r指向新的尾结点
	}
}

B头插法创建单链表
将每一个新结点插入链表的头部,头结点之后
时间复杂度为O(n)

void CreatList_H(LinkList &L,int i)
{
	L=new LNode;
	L-next=NULL;//先建立一个带头指针的空链表
	for(i=0;i<n;i++)
	{
		p=new LNode;//新结点
		cin>>p->data;//输入数据
		p-next=L->next;//将第i+1个结点即新结点的指针域指向第i个结点
		L->next=p;//新结点插入头结点之后
	}
}

七.循环链表和双向链表

1.循环链表
链表中最后一个结点的指针域指向头结点,整个链表形成一个环

与单链表的差别:终止条件不同
单链表p!=NULL或p->next!=NULL
循环链表p!=L或p->next!=NULL

利用循环链表合并两个链表

A,B是两个链表的尾结点

p=B->next->next;//p指向头结点之后的即首元结点
B->next=A->next;//将B的尾结点指针域指向A的首结点
A->next=p;//将B的首元结点连到A的尾结点之后

2.双向链表
双向链表的结点中有两个指针域,一个指向直接后继,一个指向直接前驱
定义

typedef struct DuLNode
{
 	ElemType data;
 	struct DuLNode *prior;
 	struct DuLNode  *next;
 }DuLNode,*DuLinklist;

双向链表的插入

Status ListInsert(DuLinkList &L,int i,ElemType e)
{
	if(!(p=GetElem(L,i)))//确定第i个元素的位置指针
	return error;
	s=new DuLNode;//创建新结点
        s->data=e;
	s->piror=p->piror;//将s的前驱指向p的前驱
	p->piror->next=s;//将p的前驱的后继指向s
        s->next=p;//将s的后继指向p
	p->piror=s;//将p的前驱指向s
	return ok;
}

双向链表的删除

Status Listdelete(DuLinkList &L,int i)
{
	if(!(p=(GetElem_DuL(L,i)))
	return error;
	p->piror->next=p->next;
	p->next->piror=p->piror;
	delete p;
	return ok;
}

八.顺序表和链表的比较

1.顺序表的存储空间必须预先分配,受到限制,易造成空间浪费或空间不够
当线性表的长度变化较大,难以估计,宜采用链表作为存储结构
2.存储密度=数据元素本身占用的存储量/结点结构占用的存储量
存储密度越大,存储空间的利用率越高
顺序表的存储密度是1,单链表存储密度为0.5
当线性表的长度变化不大,易于事先确定时,为了节约存储空间,宜采用顺序表
3.数组是一种随机存取结构,而链表是一种顺序存取结构
当线性表的主要操作是与位置有关的,插入和删除操作较少,宜采用顺序表
在这里插入图片描述

最后

以上就是可耐眼神为你收集整理的线性表的全部内容,希望文章能够帮你解决线性表所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部