线性表的定义和特点
定义:由N个数据特性相同的元素构成的有限序列称为线性表
特点:除第一个元素之外 结构中每一个数据元素均只有一个前驱;除最后一个元素外结构中每一个元素只有一个后继。
线性表的顺序存储表示和实现
顺序表定义:线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素称这种存储结构的线性表为顺序表
特点:逻辑上相邻的数据元素 物理次序上也是相邻的,线性表的顺序存储结构是一种随机存取的存储结构
计算
线性表的每一个元素占用t个存储单元 则第i+1个数据元素的存储位置和第i个数据元素的存储位置之间满足关系:loc(a_{i+1})=loc(a_{i})+t
所有数据元素存储位置之间满足关系:loc(a_{i})=loc(a_{1})+(i-1)*t
基本操作
顺序表的初始化: `Status initlist(sqlist &L)
{///构造一个空的顺序表L
L.elem=new ElemType[MAXSIZE]; //为顺序表分配一个大小为maxsize的数组空间
if(!L.elem) exit (overflow); //存储分配失败退出
L.length=0; //空表的长度为0
return ok;}`
顺序表的存储结构:`#define MAXsize 100 ///顺序表可能达到的最大长度
typedef struct
{
ElemType *elem; //存储空间的基地址
int length; //当前长度
}Sqlist; ///顺序表的结构类型为Sqlist
` 线性表存储到存储单元中 逻辑位序与物理位序相差1;
顺序表的取值:`Status GetElem(Sqlist L,int i,ElemType &e)
{
if(i>1||i>length) return error; //判断i值是否合理
e=L.elem[i-1]; ///elem[i-1]单元存储第i个数据元素
return ok;
} `
顺序表的查找:`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
}`
顺序表的插入:`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.elem[i-1]=e; //将新元素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)) return error; //i值不合法
for(j=i;j<=length-1;j++)
L.elem[j-1]=L.elem[j]; //被删除元素之后的元素前移
--L.length; //表长减1
return ok;
}`
顺序表的时间复杂度:查找 插入 删除的算法平均时间复杂度为O(n) 空间复杂度为O(1)
顺序表的优点:存储密度大 可以随机存取表中任一元素
缺点:插入删除元素需要移动大量元素 浪费存储空间 属于静态存储形式,数据元素个数不能自由扩充
线性表的链式存储表示和实现
线性表的链式存储结构的特点:用一组任意的存储单元存储线性表的数据元素(存储单元可以连续也可以不连续)
结点有两个域:其中存储数据元素信息的域称为数据域;存储直接后继存储位置的域称为指针域 指针域中存储的信息称为指针或者链
链表分类:单链表 循环链表 双向链表 二叉链表 十字链表 邻接表 邻接多重表
空表:当没有头结点的时候 头指针为空表示空表,有头结点时 头结点的指针域为空时表示空表
链表增加头结点的作用:
- 便于首元结点的处理(首元结点的地址存储在头结点的指针域中)
- 便于空表和非空表的统一处理
单链表是非随机存取的存储结构 取得第i个数据元素必须从头指针出发顺链寻找 也称为顺序存取的存取结构。
单链表的插入:一般情况 在第i 个元素插入一个元素时 需要从最后一个元素即第n个元素开始依次向后移动一个元素 直至第i个元素(一共需要移动n-i+1个元素)
基本操作:
单链表的初始化:`Status InitList(LinkList &L)
{ ///构造一个空的单链表L
L=new LNode; //生成新结点作为头结点 用头指针L指向头结点
L->next=NULL; //头结点的指针域置空
return ok;
}`
单链表的取值:`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;
}
if(!p||j>i) return error; //i值不合法i>n或者i<=0
e=p->data; //取第i个结点的数据域
return ok;}`
单链表的按值查找:`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
}`
单链表的插入:`Status ListInsert(ListList &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;
}`
单链表的删除:`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; //当i>n或i<1时删除位置不合理
q=p->next; //临时保存被删除结点的地址以备释放
p-next=q->next; //改变删除结点前驱结点的指针域
delete q; //释放删除结点的空间
return ok;
}`
前插法创建单链表:`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-data; //输入元素值赋给新节点*p的数据域
p->next=L-next; //将L-NEXT后面的数据元素赋给新节点p的后继
L-next=p; //将新节点p插入到头结点之后
}`
后插法创建单链表:`void CreateList_H(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-data; //输入元素值赋给新节点*p的数据域
p->next=NULL;
r->next=p; //将新节点*p插入尾结点*r之后
r=p; //r指向新的尾结点*p
}`
双向链表:双向链表的结点中有两个指针域 一个指向后继一个指向前驱
双向链表的存储结构:`typedef struct DuLNode
{
ElemType data; //数据域
struct DULNode *prior; //指向直接前驱
struct DULNode *next; //指向直接后继
}DuLNode, *DuLinkList;`
有序表:若有序表的数据元素相互之间可以比较 并且数据元素在线性表中依值非递减或非递增有序排列 则称该线性表为有序表
算法设计:
1.有序表的合并问题: 将两个递增的有序链表合并为一个递增的有序链表 要求结果链表仍使用原来两个链表的存储空间,不占用另外的存储空间。表中不允许有重复的结构
`void MergeList(LinkList &La,LinkList &Lb,LinkList &Lc) //合并链表La和Lb,合并后新表使用头部指针Lc指向
{
pa=LA->next; pb=LB->next; //pa和pb分别是链表LA和LB的工作指针 初始化为相应链表的第一个结点
LC=pc=La; ///用La的头结点作为Lc的头结点
while(pa&&pb)
{
if(pa->data<pb->data) //取较小者La元素
{ pc->next=pa; //将pa链接到pc的后面
pc=pa; //pc指向pa
pa=pa->next; ///pa的指针后移
}
else if(pa->data>pb->data) {
pc->next=pb; //将pb链接到pc的后面
pc=pa; //pc指向pb
pa=pa->next; ///pb的指针后移
}
else ///相等时取La中的元素 删除Lb中的元素
{
pc->next=pa; //将pa链接到pc的后面
pc=pa; //pc指向pa
pa=pa->next; ///pa的指针后移
q=pb->next; //保存pb的地址在q中
delete pb; //释放pb的存储空间
pb=q ///将pb指针后移
}
}
pc->next=pa? pa :pb; ///当一个表为空时 则插入剩余表段
delete Lb; ///释放Lb的头结点
} `
2.设计算法 通过一趟遍历确定长度为n的单链表中值最大的结点
`ElemType Max(LinkList &L ,int n)
{ if(L-next==Null) return NUll; ///判断是不是空表
pmax=L->next; //假定第一个结点的数据具有最大值
p=L-next-next; //下一个结点
while(p!=Null) { //如果下一个结点存在的话
if(p->data>pmax->data)
pmax=p; //如果p的值大于Pmax的值,则重新赋值
p=p->next; ///遍历链表
}
return pmax-data;}`
3.双向循环链表问题:已知p指向双向循环链表中的一个结点 其结点结构为data prior next 三个域 写出算法change(p) 交换p所指向的结点及其前驱结点的顺序
t | q | p | 交换前 |
---|---|---|---|
t | p | q | 交换后 |
`void Exchange (LinkedList p) // p是双向循环链表的一个结点 算法将p所指结点与其前驱结点交换
{ q=p->prior; //q为P的前驱
q->prior->next=p; //q的前驱的后继为p 交换后(t的后继更改)
p-prior=q->prior; //交换后p的前驱为交换前 q的前驱
q->next=p->next; ///交换后q的后继是交换前p的后继
q-prior=p; ///交换后p为q 的前驱
p->next-prior=q; ///前p的后继现在的前驱为q
p->next=q; //现在p的后继是q
} ` //结束