线性表的顺序存储结构——顺序表
在C语言中用一维数组来描述顺序存储结构,如下所示。
//线性表的顺序存储结构
#define MaxSize //线性表存储空间的初始分配量
typedef struct {
ELemType data[MaxSize]; //存放顺序表中的元素
int length; //存放顺序表的当前长度
} SqList; //声明顺序表的类型
- 顺序表的初始化
算法 2.1
void InitList(SqList &L) //由于L要回传给实参,所以用引用类型
{
L.length = 0;
}
- 取顺序表中第i个元素
算法 2.2
int GetElem(SqList L, int i, ElemType &e) //返回L中第i个元素的值
{
if(i < 1 || i > L.length)
return 0; //i值不合法
else
e = L.data[i-1]; //取出第i个元素,位置data[i-1]中
return 1;
}
时间复杂度为O(1)。
- 顺序表的查找
查找顺序表L中与给定的元素x相等的元素,若找到,返回该元素在顺序表中的序号,否则返回0,表示查找失败。
算法 2.3
int Locate(SqList L,ElemType x) {
int i = 0;
while(i < L.length && L.data[i] != x)
i++;
if(i >= L.length)
return 0; //未找到返回0
else
return i + 1; //找到后返回其逻辑序号
}
时间复杂度为O(n)。
- 顺序表的插入
算法 2.4
int ListInsert(SqList &L,int i,ElemType x) { //在顺序表L中第i个位置之前插入新的元素x
int j;
if(i < 1 || i > L.length + 1) return 0; //i值不合法
for(j = L.length; j > i; j++) //向下移动元素,寻找插入位置
L.data[j] = L.data[j-1];
L.data[i-1] = x; //将新元素x放到插入的位置
L.length++; //顺序表长度增1
时间复杂度为O(n)。
- 顺序表的删除
算法 2.5
int ListDelete(SqList &L, int i) { //在顺序表L中删除第i个元素
int j;
if(i < 1 || i > L.length) return 0; //i值不合法
for(j = i; j < L.length; j++) //将结点i之后的元素前移一个位置
L.data[j-1] = L.data[j];
L.data
时间复杂度为O(n)。
- 顺序表的合并
先设C为空表,然后比较A和B中的数据元素,将A或B中较小的元素插入到C中。当其中一个顺序表的数据元素均已插入到C中时,另一顺序表中的剩余元素依次插入即可。
算法 2.6
void merge(SqList A, SqList B, SqList &C)
{
int i = 0, j = 0, k = 0; //k记录顺序表C中的元素个数
while(i < A.length && j < B.length)
{
if(A.data[i] < B.data[j])
{
C.data[k] = A.data[i];
i++; k++;
}
else if(A.data[i] > B.data[j])
{
C.data[k] = B.data[j];
j++; k++;
}
else //A.data[i] = B.data[j]
{
C.data[k] = A.data[i];
i++; k++;
C.data[k] = B.data[j];
j++; k++;
}
}
while(i < A.length) //将A中剩余的元素复制到C中
{
C.data[k] = A.data[i];
i++; k++;
}
while(j < B.length) //将B中剩余的元素复制到C中
{
C.data[k] = B.data[j];
j++; k++;
}
C.length = k; //指定顺序表C的实际长度
}
时间复杂度为O(A.length + B.length)。
线性表的链式存储结构——链表
在此种存储结构中,线性表中的每一个元素以结点的形式存储。结点由数据域(存储数据元素的值)和指针域(存储数据元素的后继结点的地址)两部分构成。
在C语言中用“结构指针”来描述。
typedef struct node{
ElemType data;
struct node *next;
}SLink;
头结点:是链表的表头结点,其数据域为空,指针域指向链表的第一个结点(首结点),当链表为空时,指针域为空。
首结点:是链表的第一个结点,存放线性表的第一个数据元素。
头指针:是指向链表最前面结点的指针变量,即指向头结点,若表中无头结点,则指向首结点。如下图。
单链表
将线性表中数据元素的逻辑次序以结点的序列链接起来,并且只含有一个指针域的线性链表,称为单链表。
- 单链表的初始化
创建一个空的单链表,它只有一个头结点,由L指向它。该结点的next域为空,data域未设定任何值。
算法 2.7
void InitList(SLink *&L) //L为引用型参数
{
L = (SLink * )malloc(sizeof(SLink)); //创建头结点 *L
L->next = NULL;
}
- 单链表的销毁
单链表中的每个结点都是通过malloc函数分配的,不用时要通过free函数释放。
实现过程是,先让pre指向头结点,p指向第一个数据结点,如下图,当p不为空时循环,释放pre所指结点空间,让pre、p指针沿next域同步后移一个结点。当循环结束,p为空,此时再释放pre所指的尾结点。
算法 2.8
void DestroyList(SLink *&L)
{
SLink *pre = L, *p = pre->next;
while(p != NULL)
{
free(pre);
pre = p; p = p->next; //pre、p同步后移
}
free(pre);
}
时间复杂度为O(n)。
- 单链表的查找
1)按序号查找(求线性表中第i个元素)
用p从头开始遍历单链表L中的结点,用计数器j累计遍历过的结点,其初值为0,当j等于i时,若p不为空,则p所指结点即为要找的结点,查找成功,算法返回1;否则算法返回0,表示未找到这样的结点。
算法 2.9
int GetElem(SLink *L, int i, ElemType &e)
{
int j = 0;
SLink *p = L; //p指向头结点,计数器j置为0
if(i <= 0) return 0; //参数i错误返回0
while(p != NULL && j < i)
{
j++;
p = p->next;
}
if(p == NULL) return 0; //未找到返回0
else
{
e = p->data;
return 1; //找到后返回1
}
}
时间复杂度为O(n)。
2)按值查找
算法 2.10
int Locate(SLink *L, ElemType e)
{
SLink *p = L->next;
int j = 1; //p指向第一个数据结点,j置为其序号1
while(p != NULL && p->data != e)
{
p = p->next;
j++;
}
if(p == NULL) return 0; //未找到返回0
else return j; //找到后返回其序号
}
时间复杂度为O(n)。
- 在单链表中插入元素
在ai-1、ai之间插入x,即修改结点ai-1的指针域,令其指向结点x,而结点x中的指针域应指向结点ai。
假设s为指向结点x的指针,p为指向结点ai-1的指针,则上述修改用语句描述为:s->next=p->next;p->next=s;如下图所示。
算法 2.11
int InsElem(SLink *&L, ElemType x, int i) //插入结点值为x的结点
{
int j = 0;
SLink *p = L, *s;
if(i <= 0) return 0; //参数i错误返回0
while(p != NULL && j < i - 1) //查找第i-1个结点*p
{
j++;
p = p->next;
}
if(p == NULL)
return 0; //未找到第i-1的结点时返回0
else //找到第i-1个结点 *p
{
s = (SLink * )malloc(sizeof(SLink));
s->data = x; //创建存放元素x的新结点 *s
s->next = p->next; //将 *s结点插入到 *p结点之后
p->next = s;
return 1; //插入运算成功,返回1
}
}
时间复杂度为O(n)。
注意:插入操作的1和2执行顺序不能颠倒,否则若先执行p->next=s,由于修改p->next值,会使原 *p 结点的后继结点的地址丢失。
- 在单链表中删除元素
在带头结点的单链表L中,删除第i个位置元素。现在单链表L中查找第i-1个结点,若未找到返回0,找到后由p指向该结点,然后让q指向后继结点(即要删除的结点),若q所指结点为空则返回0,否则删除q所指结点,并释放其占用的空间。如下图。
算法 2.12
int DelElem(SLink *&L, int i) //删除结点
{
int j = 0;
SLink *p = L, *q;
if(i <= 0) return 0; //参数i错误返回0
while(p != NULL && j < i - 1) //查找第i-1个结点
{
j++;
p = p->next;
}
if(p == NULL) return 0; //未找到第i-1个结点时返回0
else //找到第i-1个结点 *p
{
q = p->next; //q指向被删结点
if(q == NULL) return 0; //没有底i个结点时返回0
else
{
p->next = q->next; //从单链表中删除 *q 结点
free(q); //释放其空间
return 1;
}
}
}
时间复杂度为O(n)。
- 创建单链表
假设给定一个含有n个元素的数组,由它来创建单链表。
该方法从一个空单链表(含有一个L指向的头结点)开始,读取数组a(含有n个元素)中的一个元素,生成一个新结点 *s,将读取的数据元素存放到新结点的数据域中,然后将新结点插入到当前链表的表尾上,如下图;再读取数组a的下一个元素,采用相同的操作建立新结点 *s,并插入到单链表L中,直到数组a中所有元素读完为止。
算法 2.13
void CreateListR(SLink *&L, ElemType a[], int n)
{
SLink *s, *tc;
int i;
L = (SLink * )malloc(sizeof(SLink)); //创建头结点
tc = L; //tc始终指向尾结点,初始时指向头结点
for(i = 0; i < n; i++)
{
s = (SLink * )malloc(sizeof(SLink));
s->data = a[i]; //创建存放a[i]元素的新结点 *s
tc->next = s;
tc = s;
}
tc->next = NULL; //尾结点next域置为NULL
}
时间复杂度为O(n)。
- 单链表的合并
设ha和hb分别是两个带头结点的有序递增单链表。将这两个单链表合并成一个包含所有数据点的新的有序递增单链表hc。要求hc仍使用ha和hb的存储空间,不另外占用其他的存储空间,ha和hb中允许有重复的数据。
算法思想:用pa遍历ha的数据结点,pb遍历hb的数据结点,将