数据结构 之 线性表
Posted ITWEL
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 之 线性表相关的知识,希望对你有一定的参考价值。
(一)定义
通俗定义
零个或多个元素的有序序列。
(二)存储结构
I.顺序存储结构
理解:通过连续地址的空间来进行存储元素,其实质就是一个数组
三大属性:
(1)数组的存储位置:数组data,它的存储位置就是存储空间的存储位置
(2)线性表的最大存储容量:数组长度MaxSize
(3) 线性表的当前长度:length
优点:
(1)地址连续(预先分配的地址连续的空间)
(2)依次存放(根据地址的先后进行存储元素)
(3)随机存取(根据下标来进行查找元素)
(4)类型相同(存储的元素类型均是相同的)
缺点:
(1)存储分配需要事先进行
(2)浪费空间
(3)在进行插入和删除操作时,需要最大量移动元素,时间效率低下
(1)基本结构
#define MAXSIZE 100;
typedef struct
EleType *elem; //存储空间的基地址
int length; //当前长度,用来记录所存储元素的个数
SqList
SqList L; //方式一:在调用成员时,使用L.elem[i]的方法
SqList*L; //方式二:在调用成员时,使用L->elem[i]的方法
(2)基本操作
总体包含: 初始化、增加(插入)、删除、查找、修改、排序
初始化
Status InitLIst(Sqlist &L)
//构造一个空的顺序表
L.elem = new ElemType[MAXSIZE]; //为顺序表分配一个大小为MAXSIZE的空间
if(!L.elem)exit(OVERFLOW); //存储空间分配失败
L.length=0; //空表长度为0
return OK;
查找
方式一:下标法
描述:查询第i个位置(如果存储在,下标为<i-1>)的元素,如果存在,将值赋值给e,反之就结束
Status GetElem(SqList L,int i,ElemType &e)
if(i<1||i>L.length) return ERROR;//检查位置,如果不合理,直接返回错误
e=L.elem[i-1]; //将L.elem[i-1]的值赋值给e
return OK;
方式二:关键字法
描述:查找关键字key,如果查找成功,就返回下标,反之,就返回错误
int GetElem(SqList L,int key)
for(int i=0;i<L.length;i++)
if(key==L.elem[i])
return i+1; //数组的下标是从0开始的,所以要进行加1
return Error;
时间复杂度分析:
插入(添加)
Status ListInsert(SqList &L,int i,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) //存储空间已满
return ERROR;
for(j=L.length-1;j>=i-1;j--)
L.elem[j+1]=L.elem[j]; //插入位置及之后的元素后移
L.lem[i-1]=e; //将新元素放入第i个位置
++L.length; //表长加1
return OK;
复杂度分析:
删除(覆盖)
Status listDelete(SqList &L,int i)
/*在顺序表L中删除第i个元素,i值的合理范围是1=<i<=L.length*/
if((i<1)||(i>L.length)//i值不合理
return ERROR;
for(j=i;j<L.length-1;j++)
L.elem[j-1]=L.elem[j];
--L.length; //被删除元素之后的元素前移
return OK; //表长减1
时间复杂度分析:
排序(此处以冒泡排序为例,升序排列)
Status ListSorted(SqList &L)
for(int i=0;i<L.length;i++) //控制比较的趟数
for(int j=0;j<L.length-i-1;j++)//控制每趟比较的次数
if(L.elem[j]>L.elem[j-1])
int temp=L.elem[j];
L.elem[j]=L.elem[j-1];
L.elem[j-1]=temp;
总结:由于顺序表在进行插入和删除操作时会移动大量元素,时间复杂度比较高。
II.链式存储结构
链式存储结构,元素的存储位置并不是连续的,这样就很好的避免了在删除节点时所所需要大量移动元素所造成的时间复杂度较高的问题。
(1)基本结构
缺点
在进行查找操作时,需要从前往后进行查找,无法使用“下标法”
存储结构:
typedef struct _LNode
Student data; //数据域
struct _LNode* next; //指针域(存储下一个结点的地址或NULL)
LNode,*LinkList; //别名解释:typedef将struct LNode名字改为LNode
LinkList L;//L指的是指向某结点的指针变量
LNode *L;// *L为对应的结点变量,表示该结点的名称
头结点:为了方便进行操作而引入的一个结点,数据域一般不存储数据,指针域指向下一个结点或NULL。
好处:
(1)便于首元结点的处理
(2)便于空表和非空白的统一处理
首元结点:指的是在头结点后的第一个结点。
(2)基本操作
初始化
Status InitList(LinkList L)
//构造一个空表
L=new LNode; //生成新结点作为头结点,头指针L指向头结点
L->next=NULL; //指针域置空
return OK;
插入
方式一:头插法
void CreateList_H(LinkList &head,int n)
L=new LNode; //生成头结点
L->next=NULL; //指针域置空
LinkList head=L; //将head也指向头结点
LinkList rear=L; //rear用来一直指向尾结点
for(int i=0;i<n;i++)
p=new LNode; //生成新结点
p->data=data; //初始化节点数据域
p->next=head->next;//前插法插入新结点
head->next=p;
rear=p;
说明:在进行插入新结点的时候,一定要保持原单链表不断
方式二:尾插法
void CreateList_H(LinkList &head,int n)
L=new LNode; //生成头结点
L->next=NULL; //指针域置空
LinkList head=L; //将head也指向头结点
LinkList rear=L; //rear用来一直指向尾结点
for(int i=0;i<n;i++)
p=new LNode; //生成新结点
p->data=data; //初始化节点数据域
p->next=NULL;//初始化新结点的指针域
rear->next=p; //后插法插入新结点
rear=p;
查找(修改)
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;
删除
Status ListDelete (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; //删除位置不合理
q=p->next; //临时保存待删除的结点的地址以备释放
p->next=q->next; //改变删除结点的前驱结点的指针域
delete q; //释放待删除结点的空间
return OK;
排序
说明:使用冒泡排序进行排序,与数组排序不同的是,数组可以通过下标来进行移动和进行基本操作,但是对于单链表而言,需要使用双指针来进行移动,进行操作。
Status SortedList(LNode *L)
LNode *p,*q;
int t;
int length=LengthList(L)-1;
while(length)
p=L->next;
q=p->next;
while(q!=NULL)
if(p->data>q->data)
t=p->data;
p->data=q->data; //交换指针所指向的元素值
q->data=t;
p=q; //指针后移
q=q->next;
else
p=q;
q=q->next;
length--;
if(length==0)
printf("排序成功!\\n");
以上是关于数据结构 之 线性表的主要内容,如果未能解决你的问题,请参考以下文章