2.1 线性表抽象数据类型
线形表:一种可以在任意位置进行插入和删除数据元素操作的、由n(n>=0)个相同类型数据元素a0,a1,a2,……,a(n-1)组成的线性结构。
线性表可以用顺序存储结构(顺序表)和链式存储结构(链表)存储。
线性表的抽象数据类型:数据集合和操作集合
数据集合:线性表的数据集合可以表示为a0,a1,a2,……,a(n-1)或a1,a2,a3,……,a(n),每个数据元素的数据类型为抽象数据类型DataType.
操作集合:
- 初始化ListInitiate(L)
- 求当前数据元素个数ListLength(L)
- 插入数据元素ListInsert(L,i,x) :在线性表L的第i个数据前插入数据元素x
约束条件0<=i<= ListLength(L)-1 分析i:当i=0表示在a0前插入数据元素x;当i= ListLength(L)-1表示在a(ListLength(L)-1)后插入数据元素x
- 删除数据元素ListDelete(L,i,x):删除线性表L的第i个数据元素,所删除数据由参数x带回,删除成功返回1,否则返回0
约束条件0<=i<= ListLength(L)-1 分析i:当i=0表示删除数据元素a0,所删除数据由参数x带回;当i= ListLength(L)-1表示删除数据元素a(ListLength(L)-1)
- 取数据元素ListGet(L,i,x):取出线性表L的第i个数据元素,所取数据由参数x带回,取出成功返回1,否则返回0
约束条件0<=i<= ListLength(L)-1 分析i:当i=0表示取数据元素a0;当i= ListLength(L)-1表示取数据元素a(ListLength(L)-1)
2.2 线性表的顺序表示和实现
顺序表的存储结构P29
定义结构体SeqList:
typedef struct
{
DataType list[MaxSize]; /*存放数据的数组元素成员*/
int size; /* 记录数组当前个数的成员*/
}SeqList; /*命名该结构体为SeqList*/
顺序表
顺序表操作的实现(P30~P32):
(1)顺序表初始化
void ListInitiate(SeqList *L) /*初始化函数*/
{
L->size=0; /*初始化数据元素个数*/
}
(2)求数组当前元素个数
int ListLength(SeqList L) /*求数组当前元素个数函数*/
{
return L.size;
}
(3)插入数据元素
/*在数组的位置i(0<=i<=size)前插入数组元素x函数*/
/*插入成功时函数返回1,否则返回0*/
int ListInsert(SeqList *L,int i,DataType x)
{
int j;
if(L->size>=MaxSize)
{
printf("数组已满无法插入!\n");
return 0;
}
else if(i<0||i>L->size)
{
printf("参数i不合法!\n");
return 0;
}
else
{
/*为插入做准备*/
for(j=L->size;j>i;j--)
L->list[j]=L->list[j-1];
L->list[i]=x;
L->size++; /*元素个数加1*/
return 1;
}
}
(4)删除数据元素
/*删除数组中第i个位置数据并存放到x中*/
/*删除的数组的值存于参数x中。取数据成功时返回1,否则返回0*/
int ListDelete (SeqList *L,int i,DataType *x)
{
Int j;
If(L->size<=0)
{
printf(“顺序表已空无数据元素可删!\n”);
return 0;
}
else if(i<0||i>L->size-1)
{
printf(“参数i不合法!”);
return 0;
}
else
{
*x=L->list[i]; /*保存删除的元素到x中*/
for(j=i+1;j<=size-1;j++) /*依次前移*/
L->list[j-1]=L->list[j];
L->size--; /*数据元素个数减1*/
return 1;
}
}
(5)取出数据元素
/*取数组中第i个位置数据的函数*/
/*取到的数组的值存于参数x中。取数据成功时返回1,否则返回0*/
int ListGet(SeqList L,int i,DataType *x)
{
if(i<0||i>L.size-1)
{
printf("参数i不合法!\n");
return 0;
}
else
{
*x=L.list[i];
return 1;
}
}
顺序表操作的效率分析(P33)
插入和删除的时间复杂度最高为O(n);其他操作与元素个数n无关,时间复杂度为O(1)
向线性表中插入一个数据元素需移动的数据元素的平均次数为n/2,
向线性表中删除一个数据元素需移动的数据元素的平均次数为(n-1)/2
顺序表的优缺点:
优点:算法简单,空间单元利用效率高
缺点:需预先确定元素个数,插入和删除操作需要移动较多的数据元素
顺序表的应用(P33)
2.3线性表的链式表示和实现
指针:指向物理存储单元地址的变量
结点:数据元素域和一个或若干个指针域组成的一个结构体
单链表:构成链表的结点只有一个指向直接后继结点的指针域
表示方法:
数据域 |
指针域 |
或 |
data |
next |
存放数据元素 |
存放下一结点的指针 |
存放数据元素 |
存放下一结点的指针 |
头指针:指向单链表的指针
头结点:头指针所指的不存放数据元素的第一个结点
带头结点单链表和不带头结点单链表的比较P38~P40
单链表
单链表的操作实现
定义单链表结点的结构体:
typedef struct Node
{
DataType data;
struct Node* next;
} SLNode;
(1)初始化ListInitiate(SLNode * *head)
void ListInitiate(SLNode * *head)
{
if( ( *head = (SLNode*)malloc(sizeof(SLNode)) )==NULL)/*如果有内存空间,申请头指针空间并使头指针head指向头结点*/
exit (1);
(*head)->next=NULL; /*置结束标记NULL*/
}
(2)求当前数据元素个数
int ListLength(SLNode *head)
{
SLNode *p=head; /**/
int size=0;
while(p->next!=NULL)
{
p=p->next;
size++;
}
return size;
}
(3)插入ListInsert(SLNode *head,int i,DataType x)
int ListInsert(SLNode *head,int i,DataType x)
{
SLNode *p,*q;
int j;
p=head;
j=-1;
while(p->next!=NULL&&j<i-1)
{
p=p->next;
j++;
}
if(j!=i-1)
{
printf("插入位置参数错误!");
return 0;
}
if((q=(SLNode*)malloc(sizeof(SLNode)) )==NULL)
exit (1);
q->next=p->next;
p->next=q;
return 1;
}
(4)删除ListDelete(SLNode *head,int i,DataType *x)
int ListDelete(SLNode *head,int i,DataType *x)
{
SLNode *p,*s;
int j;
p=head;
j=-1;
while(p->next!=NULL&&p->next->next!=NULL&&j<i-1)
{
p=p->next;
j++;
}
if(j!=i-1)
{
printf("删除位置参数错!");
return 0;
}
s=p->next;
*x=s->data;
p->next=p->next->next;
free(s);
return 1;
}
(5)取数据元素ListGet(SLNode *head,int i,DataType *x)
int ListGet(SLNode *head,int i,DataType *x)
{
SLNode *p;
int j;
p=head;
j=-1;
while(p->next!=NULL&&j<i)
{
p=p->next;
j++;
}
if(j!=i-1)
{
printf("取元素位置参数错!");
return 0;
}
*x=p->data;
return 1;
}
(6)撤销单链表Destroy(SLNode **head)
void Destroy(SLNode **head)
{
SLNode *p,*p1;
p=*head;
while(p!=NULL)
{
p1=p;
p=p->next;
free(p1);
}
*head=NULL;
}
单链表操作的效率分析(P45)
单链表的求数据元素个数操作的时间复杂度为O(n)
向单链表中插入一个数据元素需移动的数据元素的平均次数为n/2,
向单链表中删除一个数据元素需移动的数据元素的平均次数为(n-1)/2
单链表的优缺点
优点:不需要预先确定元素个数的最大个数
缺点:算法复杂,每个结点中要有一个指针域,空间单元利用率不高
单链表应用举例P(45)
循环链表:单链表的另一种形式,链表中最后一个结点的指针域为链表的第一个结点
差别:
- 初始化函数中,(*head)->next=NULL改为(*head)->next=head
- 其他函数中,循环条件p->next!=NULL和p->next->next!=NULL中NULL改为head
双向链表:每个结点除后置指针域外还有一个前驱指针域
双向链表结点的结构体定义: prior data next
|
|
|
type struct Node
{
DataType data;
struct Node *next;
struct Node *prior;
} DLNode;
双向链表的操作实现:P48~P51
- 初始化
- 插入数据元素
- 删除数据元素
- 撤销内存空间
2.4 静态链表 P51
2.5 算法设计举例 P52~P55
顺序表:
单链表: