实用指南:第3章线性表:线性表的应用(1)

3.8 线性表的应用

本节以例题的形式,说明线性表的应用方法。

1. 合并线性表

例 3.8.1 已知两个集合 AAABBB ,现要求一个新的集合 A=A∪BA=A\cup BA=AB 。例如:A={7,5,3,11}, B={2,6,3}A=\{7,5,3,11\},~B=\{2,6,3\}A={7,5,3,11},B={2,6,3} ,合并之后,A={7,5,3,11,2,6}A=\{7,5,3,11,2,6\}A={7,5,3,11,2,6}

【解】

可以利用两个线性表 LA 和 LB 分别表示集合 AAABBB (即线性表中的数据元素为集合中的成员),这样只需扩大线性表 LA,将存在于 LB 中而不存在于 LA 中的数据元素插入到 LA 中。只要从 LB 中依次取得每个数据元素,并按值在 LA 中进行查找,若不存在,则插入之。

【算法步骤】

  1. 分别获取 LA 表长 mmm 和 LB 表长 nnn
  2. 从 LB 中第 1 个元素开始,循环 nnn 次,执行以下操作:
    • 从 LB 中查找第 i (1≤i≤n)i~(1\le i\le n)i(1in) 个数据元素赋给 e
    • 从 LA 中查找元素 e ,如果不存在,则将 e 插在表 LA 的最后。

因为上述算法中,“将 e 插在表 LA 的最后”,如果采用顺序表,也不涉及到数据的移动,所以,本算法在具体实现时既可采用顺序表,也可采用链表形式。

【算法描述】

以下算法描述,以顺序表为例。

// 优化后的合并算法
void OptimizedMergeList(SqList &LA, SqList LB) {
// 检查LB是否为空表
if (LB.length == 0) return;
// 检查LA是否需要扩容
if (LA.length + LB.length > MAXSIZE) {
// 这里可以添加扩容逻辑或返回错误
return ERROR;
// 假设MAXSIZE是固定值,无法扩容
}
// 直接访问数组元素,避免多次函数调用
for (int i = 0; i < LB.length; i++) {
ElemType e = LB.elem[i];
int found = 0;
// 在LA中查找元素e
for (int j = 0; j < LA.length; j++) {
if (LA.elem[j] == e) {
found = 1;
break;
}
}
// 如果未找到,则添加到LA末尾
if (!found) {
LA.elem[LA.length] = e;
LA.length++;
}
}
}

【算法分析】

  • 时间复杂度是 O(m×n)O(m\times n)O(m×n) (m、n 分别为两个顺序表的长度)。

  • 空间复杂度:O(1)O(1)O(1)

2. 有序表的插入

若线性表中的数据元素相互之间可以比较,并且数据元素在线性表中依值非递减或非递增有序排列,则称该线性表为有序表(Ordered List)。有序表可以使用顺序表或链表存储。

例 3.8.2 向非递减的有序表中插入一个元素。已知有序表 L 和元素 e ,将 e 插入到 L 中,并且 L 依然是有序表。

【解】

(1)用顺序表存储有序表

【算法步骤】

  1. 从表头开始遍历有序表 L,比较位置 iii 的数据元素与 e 的大小,若大于 e 则找到插入位置 iii
  2. 将位置 iii 及后面的元素后移一个位置,将元素 e 插入在位置 iii

可以将以上步骤理解为顺序表中“查找元素”和“插入元素”两种算法的合并,只不过此处查找的是顺序表中元素第一个大于 e 的位置。

【算法描述】

//顺序表存储结构
typedef struct{
ElemType *elem;
//存储空间的基地址
int length;
//当前长度
}SqList;
//顺序表的结构类型为 SqList
Status ListInsert(SqList &L, ElemType){
int i = 0, j;
while(i < L.length && L.elem[i] < e)
i++;
//找到第一个大于 e 的位置,i 是索引,不是位序号
//将 elem[i] 及后面的元素后移一个位置
for(j = L.lenght; j > i; j--)
L.elem[j] = L.elem[j-1];
L.elem[i] = e;
//在索引 i 处放置 e
L.length++;
}

以上算法描述中,先找到插入位置(while 循环),再移动元素(for 循环)。可以用下述算法描述,将二者合二为一。

Status OptimizedListInsert(SqList &L, ElemType e) {
if (L.length >= MAXSIZE) return ERROR;
// 检查空间是否已满
int j;
for (j = L.length; j >
0 && L.elem[j-1] > e; j--) {
L.elem[j] = L.elem[j-1];
// 边比较边后移
}
L.elem[j] = e;
// 插入元素
L.length++;
return OK;
}

【算法分析】

时间复杂度:O(n)O(n)O(n)

此外,本题也可以结合后续的二分查找算法,以更高的效率找到元素的插入位置。

(2)用单链表存储有序表

在单链表中,插入元素不需要移动结点,只需要修改指针。故只需要找到第一个大于 e 的位置即可,然后通过修改指针的指向,实现本题所要求的算法。

注意,找到的应该是第一个大于 e 的结点的前驱,将 e 插入到该位置之后,这样才便于修改单链表的指针。

//单链表存储结构
typedef struct LNode{
ElemType data;
//结点的数据域
struct LNode * next;
//结点的指针域
}LNode, *LinkList;
//LinkList 为指向结构体 LNode 的指针类型
//在有序单链表中插入元素 e
void ListInsert(LinkList &L, ElemType e){
LNode *pre = L;
//查找第一个大于 e 的结点的前驱
while(pre->next != NULL && pre->next->data < e)
pre = pre->next;
//向下移动指针
LNode *s = new LNode;
//生成新结点 *s
s->data = e;
//将结点 *s 插入到 *pre 之后
s->next = pre->next;
pre->next = s;
return OK;
}

【算法分析】

时间花费在移动指针,查找到插入位置。

  • 时间复杂度:O(n)O(n)O(n)
  • 空间复杂度:O(1)O(1)O(1)
posted @ 2025-08-16 22:42  wzzkaifa  阅读(8)  评论(0)    收藏  举报