线性表
定义
由同类型数据元素构成有序序列的线性结构。
- 表中元素个数称为线性表的长度
- 线性表没有元素时,称为空表
- 表起始位置称表头,表结束位置称表尾
抽象数据类型描述
- 类型名称:线性表(List)
- 数据对象集:线性表是 n(≥0) 个元素构成的有序序列 (a1,a2,...,an)
- 操作集:线性表
L∈List
,整数
i
表示位置,元素
X∈ElementType
,线性表的主要操作有:
List MakeEmpty()
:初始化一个空线性表LElementType FindKth(int K, List L)
:根据位序K,返回相应的元素int Find(ElementType X, List L)
:在线性表L中查找X第一次出现的位置void Insert(ElementType X, int i, ListL)
:在位序i前插入一个新元素Xvoid Delete(int i, List L)
:删除指定位序i的元素int Length(List L)
:返回线性表L的长度n
顺序存储实现
利用数组的连续存储空间顺序存放线性表的个元素。
线性表的代码定义
typedef struct LNode *List;
struct LNode {
ElementType Data[MAXSIZE];
int Last;
};
struct LNode L;
List Ptrl;
- 访问下标为i的元素:
L.Data[[i]
或Ptrl->Data[i]
- 线性表的长度:
L.Last + 1
或Ptrl->Last + 1
主要操作的实现
初始化(建立空的顺序表)
List MakeEmpty() {
List PtrL;
PtrL = (List)malloc(sizeof(struct LNode));
PtrL-Last = -1;
return PtrL;
}
查找
int Find(ElementType X, List PtrL) {
int i = 0;
while (i <= PtrL->Last && PtrL->Data[i] != X)
i++;
if (i > PtrL->Last) // 说明没有找到,返回-1
return -1;
return i; // 找到后返回的是存储位置
}
查找成功的平均比较次数为 (n+1)/2 ,平均时间性能为 O(n)
插入
在第
i(i≤i≤n+1)
个位置插入一个值为X的新元素
void Insert(ElementType X, int i, List PtrL) {
int j;
if (PtrL->Last == MAXSIZE - 1) { // 表空间已满,不能插入
printf("表满");
return;
}
if (i < 1 || i > PtrL->Last + 2) { // 位置只能从第一个到末尾之间
printf("位置不合法");
return;
}
for (j = PtrL->Last; j >= i - 1; j--) // 将ai ~ an倒序向后移动
PtrL->Data[j + 1] = PtrL->Data[j] ;
PtrL->Data[i - 1] = X; // 新元素插入
PtrL->Last++; // Last仍然指代最后元素的下标
}
元素的向后移动:
* 平均移动次数为
n/2
* 平均时间性能为
O(n)
删除
删除表的第
i(1≤i≤n)
个位置的元素
void Delete(int i, List PtrL) {
int j;
if (i < 1 || i > PtrL->Last + 1) {
printf("不存在第%d个元素", i);
return;
}
for (j = i; j <= PtrL->Last; j++)
PtrL->Data[j - 1] = PtrL->Data[j]; // 将ai+1 ~ an 顺序向前移动
PtrL->Last--; // Last仍然指代最后元素的下标
}
链式存储实现
不要求逻辑上相邻的两个元素物理上也相邻。通过“链”建立起数据元素之间的逻辑关系。
线性表的代码定义
typeof struct LNode *List;
struct LNode {
ElementType Data;
List Next;
};
struct LNode L;
List PtrL;
主要操作的实现
求表长
int Length(List PtrL) {
List p = PtrL; // p指向表的第一个结点
int len = 0;
while (p != NULL) {
p = p->Next;
len++; // 当前P指向的是第len个结点
}
return len;
}
时间性能为 O(n)
查找
按序号查:FindKth
List FindKth(int K, List PtrL) {
List p = PtrL;
int i = 1;
while (p != NULL && i < k) {
p = p->Next;
i++;
}
if (i == k) // 说明找到第K个元素
return P;
return NULL;
}
平均时间性能为 O(n)
按值查找:Find
List Find(ElementType X, List PtrL) {
List p = PtrL;
while (p != NULL && p->Data != X) // 如果在链表中没有元素等于X,那么会返回NULL
p = p->Next;
return p;
}
平均时间性能为 O(n)
插入
在第
i−1(1≤i≤n+1)
个结点后插入一个值为X的新结点:
1. 先构造一个新结点,用s指向
2. 再找到链表的第
i−1
个结点,用p指向
3. 然后修改指针,插入结点(p之后插入新结点是S)
// 不带头结点的链表
List Insert(ElementType X, int i, List PtrL) {
List p, s;
if (i == 1) { // 新结点插入在表头
s = (List)malloc(sizeof(struct LNode)); // 创建一个新结点
s->Data = x;
s->Next = PtrL;
return s;
}
p = FindKth(i - 1, PtrL); // 查找到第i-1个结点
if (p == NULL) { // 第i-1个不存在,不能插入
printf("参数i错误");
return NULL;
}
s = (List)malloc(sizeof(struct LNode)); // 创建一个新结点
s->Data = x;
s->Next = p->Next; // 新结点插入在第i-1个结点后面
p->Next = s;
return PtrL;
}
平均查找次数为 n/2 ,平均时间性能为 O(n)
删除
删除链表的第
i(1≤i≤n)
个位置的结点:
1. 先找到链表的第
i−1
个结点,用p指向
2. 再用指针s指向要被删除的结点(p的下一个结点)
3. 然后修改指针,删除s所指结点
4. 最后释放s所指结点的空间
List Delete(int i, List PtrL) {
List p, s;
if (i == 1) { // 如果删除的是表的第一个结点
s = PtrL; // s指向第一个结点
if (PtrL != NULL) // 从链表中删除
PtrL = PtrL->Next;
else
return NULL;
free(s);
return PtrL;
}
p = FindKth(i - 1, PtrL); // 查找第i-1个结点
if (P == NULL || p->Next == NULL) {
printf("第%d个结点不存在", i - 1);
return NULL;
}
s = p->Next; // s指向第i个结点
p->Next = s->Next; // 从链表中删除
free(s); // 释放被删除的结点
return PtrL;
}
平均查找次数为 n/2 ,平均时间性能为 O(n)
广义表(Generalized List)
- 广义表是线性表的推广
- 对于线性表而言,n个元素都是基本的单元素
- 广义表中,这些元素不仅可以是单元素也可以是另一个广义表
代码定义
typedef struct GNode *GList;
struct GNode {
int Tag; // 标志域:0表示结点是单元素,1表示结点是广义表
union { // 子表指针域Sublist与单元素数据域Data复用,即共用存储空间
ElementType Data;
GList SubList;
} URegion;
GList Next; // 指向后继结点
};
多重链表
链表中的结点可能同时隶属于多个链。
- 多重链表中结点的指针域会有多个,如前面例子包含了Next和SubList两个指针域
- 但包含两个指针域的链表并不一定是多重链表,比如双向链表不是多重链表
多重链表有广泛的的用途:基本上如树、图这样相对复杂的数据结构都可以采用多重链表方式实现存储。
多重链表存储矩阵
采用一种典型的多重链表——十字链表来存储稀疏矩阵:
* 只存储矩阵非0元素项,结点的数据域:行坐标Row、列坐标Col、数值Value
* 每个结点通过两个指针域,把同行、同列串起来:
* 行指针(或称为向右指针)Right
* 列指针(或称为向下指针)Down
- 用一个标识域Tag来区分头结点与非0元素结点
- 头结点的标识域为”Head”,矩阵非0元素结点的标识值为”Term”