2.1 线性表及其逻辑结构
2.1.1 线性表的定义
线性表是一个具有相同特性的数据元素的有限序列
2.2.1 线性表的顺序存储—顺序表
线性表顺序存储结构:把线性表中的所有元素按照顺序存储方法进行存储。按逻辑顺序依次存储到存储器中一片连续的存储空间中。
顺序表类型声明:
typedef struct{
ElemType data[MaxSize]; //存放线性表中的元素
int length; //存放线性表的长度
}SqList; //顺序表类型
2.2.2 顺序表基本运算的实现
1.建立顺序表
整体创建顺序表,即由数组元素a[0..n-1]创建顺序表L。
void CreateList(SqList * &L,ElemType a[],int n){ //由a中的n哥元素建立顺序表
int i = 0,k = 0; //k表示L中元素的个数初始值为0
L = (SqList *)malloc(sizeof(SqList)); //分配存放线性表的空间
while (i < n){ //i扫描数组a的元素
L->data[k] = a[i];
k++; i++; //k 记录插入到 L 中的元素个数
}
L->length = k; //设置L的长度k
}
2.顺序表基本运算算法
1)初始化线性表:InitList(&L)
构造一个空的线性表 L 。实际上只需将 length 成员设置为 0 即可。
void InitList(SqList * &L){
L = (SqList *)malloc(sizeof(SqList)); //分配存放线性表的空间
L->length = 0; //置空线性表的长度为0
}
2)销毁线性表:DestoryList(&L)
释放线性表 L 占用的内存空间。
void DestoryList(SqList * &L){
free(L); //释放L所指的顺序表空间
}
3)判断线性表是否为空表:ListEmpty(L)
返回一个值表示 L 是否为空表。若 L 为空表,则返回 true ,否则返回 false 。
bool ListEmpty(SqList *L){
return(L->length==0);
}
4)求线性表的长度:ListLength(L)
返回顺序表 L 的长度。实际上只需返回 length 成员的值即可。
int ListLength(SqList *L){
return(L->length);
}
5)输出线性表:DispList(L)
该运算当线性表 L 不为空时,顺序显示 L 中各元素的值。
void DispList(SqList *L){
for (int i = 0; i < L->length; i++)
printf("%d ",L->data[i]);
printf("\n");
}
6)按序号求线性表中的元素:GetElem(L ,i ,&e)
bool GetElem(SqList *L,int i,ElemType &e){
if (i < 1 || i > L->length)
return false; //参数i错误时返回false
e = L->data[i-1]; //取元素的值
return true; //成功找到元素时返回true
}
7)按元素值查找:LocateElem(L ,e)
int LocateElem(SqList *L,ElemType e){
int i = 0;
while(i < L->length && L->data[i] != e)
i++; //查找元素e
if (i >= L->length)
return 0; //未找到时返回0
else
return i+1; //找到返回其逻辑序号
}
8)插入数据元素:ListInsert(&L ,i ,e)
bool ListInsert(SqList *&L,int i,ElemType e){
int j;
if (i < 1 || i > L->length + 1 || L->length == MaxSize)
return false; // 参数错误时返回 false
i--; // 将顺序表逻辑序号转化为物理序号
for (j = L->length; j > i; j--) // 将data[i..n]元素后移一个位置
L->data[j] = L->data[j-1];
L->data[i] = e; // 插入元素 e
L->length++; // 顺序表长度增 1
return true; // 成功插入返回 true
}
9)删除数据元素:ListDelete(L,i,e)
bool ListDelete(SqList *&L , int i , ElemType &e){
int j;
if (i < 1 || i > L->length) // 参数错误时返回 false
return false;
i--; // 将顺序表逻辑序号转化为物理序号
e = L->data[i];
for (j = i; j < L->length - 1; j++) // 将 data[i..n-1] 元素前移
L->data[j] = L->data[j+1];
L->length--; // 顺序表长度减 1
return true; // 成功删除返回 true
}
2.3 线性表的链式存储结构
2.3.1 线性表的链式存储结构——链表
2.3.2 单链表
单链表中结点类型 LinkNode 的声明如下 :
typedef struct LNode{
ElemType data; // 存放元素值
struct LNode *next; // 指向后继结点
}LinkNode; // 单链表结点类型
1.插入和删除结点的操作
1)插入结点的操作
2.建立单链表
1)头插法建立单链表
从一个空表开始依次读取字符数组 a 中的元素,生成一个新结点(由s指向它)将读取的数组元素存放到该结点的数据域中,然后将其插到当前链表的表头(即头节点之后),直到数组a中的所有元素读完为止。
void CreateListF(LinkNode *&L,ElemType a[],int n){
LinkNode *s;
L = (LinkNode *)malloc(sizeof(LinkNode));
L->next = NULL; // 创建头结点,其next域置为NULL
for (int i = 0; i < n; i++){ // 循环建立数据结点s
s=(LinkNode *)malloc(sizeof(LinkNode));
s->data = a[i]; // 创建数据结点 s
s->next = L->next; // 将 s 插在原开始结点之前,头结点之后
L->next = s;
}
}
本算法的时间复杂度为O(n),其中n为单链表中数据结点的个数。
2)尾插法建立单链表
从一个空表开始依次读取字符数组 a 中的元素,生成一个新结点s,将读取的数组元素存放到该结点的数据域中,然后将其插到当前链表的表尾上,直到数组a中的所有元素读完为止。为此需要增加一个尾指针r,使其始终指向当前链表的尾结点,每插入一个新结点后让r指向这个新结点,最后还需要将r所指结点(尾结点)的next域置为空。
void CreateListR(LinkNode *&L,ElemType a[],int n){
LinkNode *s,*r;
L = (LinkNode *)malloc(sizeof(LinkNode)); // 创建头结点
r = L; // r始终指向尾结点,开始时指向头结点
for(int i = 0; i < n; i++){ // 循环建立数据结点
s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = a[i]; // 创建数据结点 s
r->next = s; // 将结点s插入结点r之后
r = s;
}
r->next = NULL; // 尾结点 next 域置为 NULL
}
本算法的时间复杂度为O(n),其中n为单链表中数据结点的个数。
3.线性表的基本运算在单链表中的实现
1)初始化线性表:InitList(&L)
建立一个空的单链表,即创建一个头结点并将其next域置为空。
void InitList(LinkNode * &L){
L = (LinkNode *)malloc(sizeof(LinkNode)); // 创建头结点
L->next = NULL; // 将其next域置为NULL
}
2)销毁线性表: DestoryList(&L)
void DestoryList(LinkNode * &L){
LinkNode *pre = L,*p = L->next; // pre指向结点p的前驱结点
while(p != NULL){ // 遍历单链表L
free(pre); // 释放pre结点
pre = p; // pre、p同步后移一个结点
p = pre->next;
} // 循环结束时p为NULL
free(pre); // 释放指向尾结点的pre
}
3)判断线性表是否为空表:ListEmty(L)
bool ListEmpty(LinkNode *L){
return (L->next == NULL);
}
4)求线性表的长度:ListLength(L)
int ListLength(LinkNode *L){
LinkNode *p = L; // p指向头结点
int n = 0; // n置为0,即头节点的序号为0
while(p->next != NULL){
p = p->next;
n++;
} // 循环结束,p指向尾结点
return (n); // 其序号n为结点的个数
}
5)输出线性表:DispList(L)
void DispList(LinkNode *L){
LinkNode *p = L->next; // p指向首结点
while(p != NULL){ // p不为NULL
printf("%d",p->data); // 输出p结点的data域
p = p->next; // p移向下一个结点
}
printf("\n");
}
6)按序号求线性表中的元素:GetElem(L,i,&e)
bool GetElem(LinkNode *L,int i,ElemType &e){
int j = 0;
LinkNode *p = L; // p 指向头结点,j置为0(即头结点的序号为0)
while(j < i && p != NULL){
p = p->next;
j++;
}
if (p == NULL) // 不存在第 i 个数据结点,返回 false
return false;
else{ // 存在第 i 个数据结点,返回 true
e = p->data;
return true;
}
}
7)按元素值查找:LocateElem(L,e)
int LocateElem(LinkNode *L,ElemType e){
int i = 1;
LinkNode *p = L->next; // p 指向开始结点, i 置为 1
while (p != NULL && p->data != e){
p = p->next; // 查找 data 值为 e 的结点,其序号为 i
i++;
}
if (p == NULL) // 不存在元素值为 e 的结点,返回 0
return(0);
else // 存在元素值为 e 的结点,返回其逻辑序号 i
return(i);
}
8)插入数据元素:ListInsert(&L,i,e)
bool ListInsert(LinkNode *&L,int i,ElemType e){
int j = 0;
LinkNode *p = L,*s; //p 指向头结点, j 置为 0
while (j < i - 1 && p != NULL) {
p = p->next;
j++;
}
if (p == NULL) // 未找到第 i-1 个结点,返回 false
return false;
else { // 找到第i-1个结点p,插入新结点并返回true
s = (LinkNode *)malloc(sizeof(LinkNode));
s->data = e; // 创建新结点 s ,其 data 域置为 e
s->next = p->next; // 将 s 插入到 p 之后
p->next = s;
return true;
}
}
9)删除数据元素:ListDelete(&L,i,&e)
bool ListDelete(LinkNode *&L,int i,ElemType &e){
int j = 0;
LinkNode *p = L,*q; // p 指向头结点, j 置为 0
while (j < i-1 && p != NULL){ // 查找第 i-1 个结点
p = p->next;
j++;
}
if (p == NULL) // 未找到第 i-1 个结点,返回 false
return false;
else { // 找到第 i-1 个结点 p
q = p->next; // q 指向第 i 个结点
if (q == NULL) // 若不存在第 i 个结点,返回 false
return false;
e = q->data;
p->next = q->next; // 从单链表中删除 q 结点
free(q); // 释放 q 结点
return true; // 返回 true 表示成功删除第 i 个结点
}
}