第二章:1.线性表 -- 线性表的顺序表示及实现
线性结构的特点是在数据的非空有限集中:
1)存在唯一一个被称为“第一个”的数据元素
2)存在唯一一个被称为“最后一个”的数据元素
3)除了第一个元素之外,集合中每个数据元素均只有一个前驱
4)除最后一个元素外,集合中每个数据元素均只有一个后继
1.线性表的类型定义
2.线性表的顺序表示和实现
1.线性表的类型定义
线性表(Linear_List)是最常用且最简单的一种数据结构。简言之,一个线性表是n个数据元素的有限序列。至于每个数据元素可以是一个数,一个符号,也可以是一页书,甚至更复杂的信息。
字符表:(A,B,C,D......Z)是一个线性表
在稍复杂的线性表中 ,一个数据元素可以由若干数据项(item)组成。在这种情况下,常把数据元素称为记录(record),含有大量记录的线性表又称文件。
例如,一个学校学生健康情况登记表,表中每个学生的情况为一个记录,它由姓名、学号、性别、年龄、健康状况等数据项组成。
综上所述,线性表中的数据元素各式各样,但同一线性表中的元素必定具有相同的特性,即同属一个数据对象,相邻数据元素之间存在着序偶关系。
若将线性表记为:
(a1, a2, .....ai.....,an)
则 n定义为线性表的长度,n=0时称为空表。在非空表中的每个数据元素都有一个确定的位置,如a1是第一个数据元素,an是最后一个,ai为第i个元素,称i为ai在线性表中的位序。
线性表是一个相当灵活的数据结构,它的长度可根据需要增长或缩短,即对线性表的数据元素不仅可以进行访问,还可进行插入和删除等。
2.线性表的顺序表示和实现
线性表的顺序表示是指用一组地址连续的存储单元依次存储线性表的数据元素。
若每个元素占用l个存储单元,线性表的第i个元素ai 的存储位置
LOC(ai)=LOC(a1)+(i-1)*l
线性表的第一个元素的位置 LOC(a1)为线性表的起始地址 或 基地址。
此种存储方式称作线性表的顺序存储结构或顺序映像(sequential mapping),通常称其为顺序表。其特点是:以物理位置相邻来表示数据元素的逻辑关系。因此只要确定了存储线性表的起始位置,线性表中的任一个元素都可以随机存取,所以线性表的顺序存储结构是一种随机存取的存储结构。
线性表的插入和删除:
在线性表的顺序存储结构中,由于逻辑上相邻的数据元素在物理位置上也是相邻的,因此除非 插入或者 删除 的位置 i=n+1,否则必须移动元素才能反映这个逻辑关系的变化。
如下图2.3:在第5个位置插入一个元素,那么原表中的5-8个位置的元素都要往后移动
如下图2.4:在第4个位置处删除元素,那么原来表中的5-8个位置的元素都要往前移动
顺序表的代码实现:
#include<stdio.h>
#include<stdlib.h>
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
//Status是函数的类型,其值是函数结果状态码
typedef int Status;
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
typedef int ElemType;
typedef struct{
ElemType * elem; //存储空间基地址
int length; //当前长度
int listsize; //当前分配的存储容量(以sizeof(ElemeType)为单位)
}SqList;
//初始化顺序表
Status InitList_Sq(SqList &L){
//构造一个空的线性表L
L.elem=(ElemType *)malloc(LIST_INIT_SIZE*sizeof(ElemType));
if(!L.elem) exit(OVERFLOW); //存储分配失败
L.length=0; //空表长度为0
L.listsize=LIST_INIT_SIZE; //初始化存储容量
return OK;
}
//插入顺序表
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>=L.listsize){ //当前存储空间已满,增加分配
ElemType * newbase;
newbase=(ElemType *)realloc(L.elem,(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase) exit(OVERFLOW); //分配存储失败
L.elem=newbase; //新基址
L.listsize+=LISTINCREMENT; //增加存储容量
}
ElemType * q;
q=&(L.elem[i-1]); //q为插入位置
//将所有i之后,包括i元素的,所有元素右移
ElemType * p;
for(p=&(L.elem[L.length-1]);p>=q;p--)
* (p+1)=* p;
*q=e; //插入e
++L.length; //表长增1
return OK;
}
//删除线性表L中,i位置处的值,并用变量e返回该值
Status ListDelete_Sq(SqList &L,int i,ElemType &e){
//判断i是否合法
if(i<1||i>L.length) return ERROR; //不存在i,返回错误
e=L.elem[i-1]; //返回当前删除的值
//把i后面所有的元素左移
ElemType *q;
for(q=&(L.elem[i-1]);q<&(L.elem[L.length-1]);q++)
*q=*(q+1);
--L.length;
return OK;
}
//比较两个元素的大小
Status compare(ElemType e1,ElemType e2)
{
if (e1==e2)
return 0;
if ( e1<e2 )
return -1;
return 1;
}
//找出L中,第一个值为e的位置。若不存在,返回0
Status LocateElem(SqList &L,ElemType e,Status(*compare)(ElemType,ElemType)){
//在顺序L中查找第一个值与e满足 compare()的元素位序。
//若找到,则返回其在L中的位序,否则返回0
int i=1; //i的初始值为第一个元素的位序
ElemType * p=L.elem; //p的初始值为第一个元素的储存位置
while(i<=L.length&&(*compare)(*p++,e))
++i;
if(i<=L.length)
return i;
else
return 0;
}
//合并两个非递减的线性顺序表 La 和 Lb,使得合并后的 Lc 仍然为非递减线性顺序表
void MergerList(SqList La,SqList Lb,SqList &Lc){
//初始化两个表的基地址
ElemType * qa=La.elem;
ElemType * qb=Lb.elem;
Lc.listsize=Lc.length=La.length+Lb.length;
ElemType * qc=Lc.elem=(ElemType *)malloc(Lc.listsize*sizeof(ElemType));
if(!Lc.elem) exit(OVERFLOW); //存储分配失败
//取La和Lb中的较小值,插入到Lc中
while(qa<=(La.elem+La.length-1)&&qb<=(Lb.elem+Lb.length-1)){
if(*qa<=*qb){
*qc++=*qa++;
}
else{
*qc++=*qb++;
}
}
while(qa<=(La.elem+La.length-1)) //插入La中剩余元素
*qc++=*qa++;
while(qb<=(Lb.elem+Lb.length-1)) //插入Lb中剩余元素
*qc++=*qb++;
}
//d打印出顺序表中的所有值
void PrintAllValue(SqList &L){
if(L.length<=0)
printf("%s\n","顺序表为空表");
//遍历线性表L,打印出每个元素的内存地址和其存储的值
printf("%s\n","顺序表的内容为:");
for(ElemType * p=L.elem;p<=&(L.elem[L.length-1]);p++){
printf("当前地址为:%p,",p);
printf("当前值为:%d\n",*p);
}
return;
}
void main(int argc, char *argv[]){
SqList L;
InitList_Sq(L);
ListInsert(L,1,9);
ListInsert(L,2,1);
ListInsert(L,2,8);
ListInsert(L,1,122);
ListInsert(L,3,3);
printf("线性表长度为:%p\n",L.length);
PrintAllValue(L);
//删除
ElemType e;
if(ListDelete_Sq(L,3,e)==0)
printf("%s\n","list delete fail...");
printf("%s\n","after delete");
PrintAllValue(L);
printf("删除的值是%d\n",e);
printf("%s\n","after delete");
//判断是否存在元素
printf("L中8的位置是:%d\n",LocateElem(L,8,compare));
printf("%s\n","init la lb...");
SqList La,Lb;
InitList_Sq(La);
ListInsert(La,1,2);
ListInsert(La,2,3);
ListInsert(La,3,5);
ListInsert(La,4,9);
printf("%s\n","La:");
PrintAllValue(La);
InitList_Sq(Lb);
ListInsert(Lb,1,1);
ListInsert(Lb,2,3);
ListInsert(Lb,3,7);
ListInsert(Lb,4,8);
printf("%s\n","Lb:");
PrintAllValue(Lb);
printf("%s\n","merge list start...");
SqList Lc;
MergerList(La,Lb,Lc);
PrintAllValue(Lc);
}
运行结果:
3.总结:
线性表顺序存储结构的特点,是逻辑上相邻的元素在物理位置上也是相邻的,因此可以随机的存取任何一个元素。
然而,另一方面,这个特点也铸成了其弱点:在做插入或 删除操作时需要移动大量元素。