2. 线性表
2.1 线性表的逻辑结构
- 线性结构
线性结构的基本特征为:一个数据元素的有序(次序)集 - 线性表的抽象数据类型
线性表是一种最简单的线性结构,线性表的抽象数据类型定义如下:
ADT List{
数据对象:
D={ai|ai∈ElemSet,i=1,2,...n,n>=0}
称n为线性表的表长,称n=0时的线性表为空表
数据关系:
R1={<ai-1,ai>|ai-1,ai∈D,i=2,...,n}
{ 设线性表为(a1,a2,...,ai,....,an}称i为ai在线性表中的位序}
本操作:
结构初始化操作
结构销毁操作
引用型操作
销毁操作
}ADT;
- 线性表的基本操作
(1)结构初始化操作
InitList(&L)
(2)结构销毁操作
DestroyList(&L)
(3)引用型操作
ListEmpty(L) (线性表判空)
ListLength(L) (求线性表的长度)
PriorElem(L,cur_e,&pre_e)(求数据元素的前驱)
NextElem(L,cur_e,&next_e)(求数据元素的后继)
GetElem(L,i,&e)(求线性表某个数据元素,返回线性表中第i个元素的值)
LocateElem(L,e,compare()) (定位函数,返回L中第一个与e相等的位序)
ListTraverse(L,visit())(遍历线性表,初始条件是线性表L存在)
(4)结构初始化操作
ClearList(&L)(线性表置空)
PutElem(&L,i,e)(将e的值赋给第i个元素)
ListInsert(&L,i,e)(插入数据元素,在第i个元素前插入新的元素e,L的长度增1)
ListDelete(&L,i,&e)(删除L的第i个元素,并用e返回其值,L的长度减1)
2.1.1 例题
- 选修同一门课的学生是线性结构(√)
- 农贸市场的人群是线性结构(x)
- 将线性表置为空表,就是销毁这个线性表(x)
- 线性表的长度必然大于零(X)
- 线性表中的每个元素,既有前驱,又有后继(X)
2.2 线性表的操作
2.2.1 合并集合
假设有两个集合A和B分别用两个线性表LA和LB表示,即线性表中的数据元素即为集合中的成员,现在求 一个新的集合,要求 A=A∪B A={1,2,4,6,7} B={1,3,9,8}
操作步骤:
- 从线性表LB中依次查看每个数据元素
- 依值在线性表LA中进行查访
- 若不存在,则插入之
?? 合并两个数组
void union(List &la,List lb){
la_len=listlength(la);
lb_len=listlength(lb);
for(i=1;i<lb_len;i++){
GetElem(lb,i,e);
if(!LocateElem(la,e,equal()))
ListInsert(la,++la_len,e);
}
}
2.2.2 过滤集合中的重复元素
已知一个非纯集合B,试构造一个纯集合A,使A 中只包含B中所有各不相同的数据元素 初始:A={ },B={6,2,9,3,6} 结果: A={6,2,9,3} 上述问题可以演绎为: 将存在于集合B中的元素逐个放入空集合A中,但A中不能有重复元素,算法策略与例1相同。
?? 过滤集合中的重复元素
void union(List&la,list lb){
InitList(la);
la_len=ListLength(la);
lb_len=listlength(lb);
for(i=1;i<=lb_len;i++){
GetElem(lb,i,e);
if(!LocateElem(la,e,equal()))
ListInsert(la,++la_len,e);
}
}
2.2.3合并有序表
有序表 若线性表中的数据元素相互之间可以比较,并且数据元素在线性表中依值非递减或者非递增有序排列, 则称改线性表为有序表(Ordered List) A={1,4,5,7,9,12,15,17,20} B={2,3,6,6,8} C=A∪B={1,2,3,4,5,6,6,7,8,9,12,151,7,20}
?? 合并两个有序数组
void MergeList(List la,List lb,List&lc){
InitList(LC);
i=j=1;
k=0;
la_len=listlength(la);
lb_le=listlength(lb);
while((i<=la_len)&&(j<=lb_len)){
GetElem(la,i,ai);
GetElem(lb,j,bj);
if(ai<=bj){
ListInsert(lc,++k,ai);
++i;
}
eles{
ListInsert(lc,++k,bj);
++j;
}
}
while(i<=la_len){
GetElem(la,i++,ai);
ListInsert(lc,++k,ai);
}
while(j<=lb_len){
GetElem(lb,++j,bj);
ListInsert(lc,++k,bj);
}
}
2.3 顺序表
2.3.1线性表的顺序存储结构
length : 当前元素个数(当前已经占用的存储空间的大小) listsize : 当前分配的存储空间大小 ElemType *elem : 指向存储空间的基地址
2.3.1.1 顺序表初始化
#define LIST_INIT_SIZE 100
#define LISTINCREMENT 10
typedef struct{
ElemType *elem;
int length;
int listsize;
}SqList;
Status InitList_Sq(SqList& L){
L.elem=(ElemType*)malloc(LIST_init_SIZE*sizeof(ElemType));
if(!L.elem)exit(OVERFLOW);
L.length=0;
L.listsize=LIST_INIT_SIZE;
return OK;
}
2.3.2 顺序表的查找
int LocateElem_Sq(SqList L,ElemType e,Status (*compare)(ElemType,ElemType)){
i=1;
p=L.elem;
while(i<=L.length&&!(*compare)(*p++,e))
++i;
if(i<=L.length)
return i;
else
return 0;
}
2.3.1.3 例题
- 编程时如何定义顺序表
typedef struct{
int *elem;
int length;
int listsize;
}SqList;
- malloc函数如何使用
L.elem=(int*)malloc(LIST_INIT_SIZE*sizeof(int));
- 如何引用顺序表中的变量
2.3.2 顺序表的插入
Status LisInsert_Sq(SqList&L,int i,ElemType e){
if(i<1||i>L.length+1)
return ERROR;
if(L.length>=L.listsize){
newbase=(ElemType *)realloc(L.elem,
(L.listsize+LISTINCREMENT)*sizeof(ElemType));
if(!newbase)
exit(OVERFLOW);
L.elem=newbase;
L.listsize+=LISTINCREMENT;
}
q=&(L.elem[i-1]);
for(p=&(L.elem[L.length-1]);p>=q;--p)
*(p+1)=*p;
*q=e;
++L.length;
return OK
}
2.3.2.1例题
- 长度为n的顺序表,一共有 n+1 个插入位置,在第i个元素前插入一个新的元素,要移动的元素个数是 n-i+1
- 指针q指向顺序表的第i个元素用c语言程序表示:
q=&(L.elem[i-1]);
- 在顺序表中,指针p所指向的元素后移一位:
*(p+1)=*p;
2.3.3 顺序表的删除
Status ListDelete_Sq(SqList &L,int i,ElemType &e){
if((i<1)||(i>L.length))
return ERROR;
p=&(L.elem[i-1]);
e=*p;
q=L.elem+L.length-1;
for(++p;p<=q;++p)
*(p-1)=*p;
--L.length;
return OK
}
2.3.3.1 例题
- 长度为n的顺序表,一共有 n 个删除位置,如果要删除第i个元素,要移动的元素个数是 n-i个
- 指针q指向顺序表(表长为n)的元素用c语言程序表示
q=&(L.elem[n-1]);
q=L.elem+L.length-1;
- 在顺序表中,指针p所指向的元素前移一位如何用c语言程序表示:
*(p-1)=*p;
2.3 单链表
用一组地址任意的存储单元存放线性表中的数据元素 结点和单链表的类C语言描述
Typedef struct LNode{
ElemType data;
struct LNode *next
}LNode,*LinkList;
LinkList L;
2.3.1 生成链表
生成链表的过程是结点逐个插入的过程 ?? 头插法生成链表
void CreateList_L(LinkList& L,int n){
L=(LinkList)malloc(sizeof(LNode));
L->next=NULL;
for(i=n;i>0;--i){
p=(LinkList)malloc(sizeof(LNode));
scanf(&p->data);
p->next=L->next;
L->next=p;
}
}
2.3.2 获取元素
Status GetElem(LinkList L,int i,ElemType &e){
p=L->next;
j=1;
while(p&&j<i){
p=p->next;
++j;
}
if(!p||j>i)
return ERROR;
e=p->data;
return OK;
}
2.3.3 单链表存储结构改进
typedef struct LNode{
ElemType data;
struct LNode *next;
}*Link,*Position;
typedef struct{
Link head,tail;
int len;
Link current;
}LinkList;
2.3.4 例题
- L是单链表的头指针,L指向头结点,单链表的第一个元素如何指向
L->next; - 指针p指向单链表中的第i个元素ai,ai+2如何指向?
p->next->next; - 欲在单链表的尾部增加一个新结点,必须遍历整个链表?
要看单链表的结构而定
2.3.5 单链表的插入
- 找到线性表中第i-1个结点
- 修改其指向后继的指针
Status ListInsert_L(LinkList L,int i,ElemType e){
p=L;
j=0;
while(p&&j<i-1){
p=p->next;
++j;
}
if(!p||j>i-1)
return ERROR;
S=(LinkList)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s;
}
2.3.5.1 例题
- 指针p指向单链表的第i个元素ai,欲在ai后面连续插入两个元素m和n,写出主要算法
s=(LinkList)malloc(sizeof(LNode));
s->data=m;
s->next=p->next;
p->next=s;
p=s;
s=(LinkList)malloc(sizeof(LNode));
s->data=n;
s->next=p->next;
p->next=s;
2.3.6 单链表的删除
Status ListDelte(LinkList L,int i,Elmetype &e){
p=L; j=0;
while(p->next&&j<i-1){
p=p->next;
++j;
}
if(!(p->next)||j>i-1)
return ERROR;
q=p->next;
p->next=q->next;
e=q->data;
free(q);
return OK;
}
void ClearList(&L){
while(L->next){
p=L->next;
L->next=p->next;
free(p);
}
}
例题
- 连续删除两个元素
q=p->next;
p->next=q->next;
free(q);
q=p->next;
p->next=q->next;
free(q);
2.4 单循环链表
最后一个结点的指针域的指针又指回头结点的链表 判别链表最后一个结点的条件是: 后继结点是否指向头结点
Typedef struct LNode{
ElemType data;
struct LNode *next
}LNode,*LinkList;
void CreatList(LinkList L,int n){
int i;
Lnode *p,*s;
p=L;
for(i=1;i<n;i++){
s=(LinkList)malloc(sizeof(LNode));
s->data=i+1;
s->next=p->next;
p->next=s;
p=s;
}
}
2.5 双向链表
typedef struct DuLNode{
ElemType data;
struct DuLNode *prior;
struct DuLNode *next;
}DuLNode, *DuLinkList;
void CreatListHead(DuLinkList L,int n){
int i;
LNode *p,*s;
p=L;
for(int i=0;i<n;i++){
printf("请输入第%d个元素:",i+1);
s=(DuLinkList)malloc(sizeof(LNode));
scanf("%d",&s->data);
s->next=p->next;
s->prior=p;
p->next=s;
if(s->next)
s->next->prior=s;
}
}
2.5.1 双向链表的插入
s->next=p->next;
p-next=s;
s->next->prior=s;
s->prior=p;
2.5.2例题
在双向链表中的元素ai前插入一个新元素e,指针p指向结点ai,写出相应的算法语句
s=(DuLinkList)malloc(sizeof(DulNode));
s->data=e;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
2.5.3双向链表的删除
q =p->next
p->next=p->next->next;
q->next->prior=p;
free(q);
2.5.3.1 例题
- 删除双向链表中的元素ai,指针p指向结点ai,写出相应的算法语句
e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
2.6.1例题
2.6.1.1单循环列表的转置
Status Contrary(LinkList &L){
t=L;
p=t->next;
q=p->next;
while(p!=L){
p->next=t;
t=p;
p=q;
q=p->next;
}
L->next=t;
return OK;
}
2.6.1.2约瑟夫环
void LinkListDelete(LinkList L,int n,int m)
{
LNode *p,*s;
int i,j;
p=L;
for(i=0;i<n;i++)
{
j=1;
while(j<m-1)
{
p=p->next;
j++;
}
s=p->next;
p->next=s->next;
printf("%d",s->data);
free(s);
p=p->next;
}
}
|