《C语言数据结构》严蔚敏,吴伟民版。
上章内容链接:https://blog.csdn.net/daqino1/article/details/88703321
下章内容链接:https://blog.csdn.net/daqino1/article/details/88832307
以下内容为基础的静态链表,循环链表,双向链表。
静态链表: 数组的一个分量表示一个结点,同事用游标代替指针指示结点在数组中的相对位置。数组的第0分量可看成头结点,其指针域指示链表的第一个结点。缺点:需要预先分配一个较大的空间。优点:做线性表插入和删除操作时,不需要移动元素,仅修改指针。
定位静态链表的数据:算法2.13
//--------------------------------线性表的静态单链表存储结构---------------------------------------
#define MAXSIZE 1000 // 链表的最大长度
typedef struct {
ElemType data;
int cur;
}component, SLinkList[MAXSIZE];
int LocateElem_SL(SLinkList S, ElemType e) {
// 在静态单链线性表L中查找第1个值为e的元素
// 若找到,则返回它在L中的位序,否则返回0
// i 指示表中的第一个结点
i = S[0].cur;
while (i && S[i].data != e) {
// 在表中顺链查找
i = S[i].cur;
}
return i;
}
初始化静态单链线性表:算法2.14
void InitSpace_SL(SLinkList &space) {
for (i = 0; i < MAXSIZE-1; ++i) {
space[i].cur = i + 1;
}
space[MAXSIZE-1].cur = 0;
}
从备用空间取一个结点: 算法2.15
int Malloc_SL(SLinkList &space) {
// 若备用空间链表非空,则返回分配的结点下标,否则返回0
i = space[0].cur;
if (space[0].cur) {
space[0].cur = space[i].cur;
}
return i;
}
将空闲结点连结到备用链表上:算法2.16
void Free_SL(SLinkList &space, int k) {
// 将下标为k的空闲结点回收到备用链表
space[k].cur = space[0].cur;
space[0].cur = k;
}
在一维数组中建立表示集合(A-B)U(B-A):算法2.17
void difference(SLinkList &space, int &S) {
// 依次输入集合A和B的元素,在一维数组space中建立表示集合(A-B)U(B-A)
// 的静态链表,S为其头指针。假设备用空间足够大,space[0].cur为其头指针
// 初始化备用空间
InitSpace_SL(space);
// 生成S的头结点
S = Malloc_SL(space);
// r指向S的当前最后结点
r = S;
// 输入A和B的元素个数
scanf(m, n);
// 建立集合A的链表
for (j = 1; j <= m; ++j) {
// 分配结点
i = Malloc_SL(space);
// 输入A的元素值
scanf(space[i].data);
// 插入到表尾
space[r].cur = i;
r = i;
}
// 尾结点的指针为空
space[r].cur = 0;
// 依次输入B的元素, 若不在当前表中,则插入,否则删除
for (j = 1; j <= n; ++j) {
scanf(b);
p = S;
// k指向集合A中的第一个结点
k = space[S].cur;
// 在当前表中查找
while (k != space[r].cur && space[k].data != b) {
p = k;
k = space[k].cur;
}
// 当前表中不存在该元素,插入在r所指结点之后,且r的位置不变
if (k == space[r].cur) {
i = Malloc_SL(space);
space[i].data = b;
space[i].cur = space[r].cur;
space[r].cur = i;
} else {
// 该元素已在表中,删除
space[p].cur = space[k].cur;
Free_SL(space, k);
// 若删除的是尾元素,则需要修改尾指针
if (r == k) {
r = p;
}
}
}
}
循环链表: 单链线性表中,最后一个结点的指针指向头结点,整个链表形成一个环。从表中任一结点出发,均可找到表中其他的结点。
循环条件: 判断的方法不是p或p->next是否为空。而是是否等于头指针。
双向链表:比单链表还多一个指针域,直接可以查询到当前结点的前趋,而不用顺指针查询。
双向链表的插入和删除操作:算法2.18和算法2.19
//---------------------------线性表的双向链表存储结构--------------------------------
typedef struct DuLNode {
ElemType data;
struct DuLNode * prior;
struct DuLNode * next;
}DuLNode, *DuLinkList;
Status ListInsert_DuL(DuLinkList &L, int i, ElemType e) {
// 在带头结点的双链循环线性表L中第i个位置之前插入元素e
// i的合法值为 1 <= i <= 表长+1
// 在L中确定第i个元素的位置指针p
if (!(p = GetElemP_DuL(L, i))) {
// p = NULL, 即第i个元素不存在
return ERROR;
}
if (!(s = (DuLinkList)malloc(sizeof(DuLNode)))) {
return ERROR;
}
s->data = e;
s->prior = p->prior;
p->prior->next = e;
s->next = p;
p->prior = s;
return OK;
}
Status ListDelete_DuL(DuLinkList &L, int i, ElemType &e) {
// 删除带头结点的双链循环线性表L的第i个元素,i的合法值为1<=i<=表长
// 在L中确定第i个元素的位置指针p
if (!(p = GetElemP_DuL(L, i))) {
// p = NULL, 即第i个元素不存在
return ERROR;
}
e = p->data;
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
return OK;
}
带头结点的线性链表定义,插入和合并操作: 算法2.20和2.21
// 结点类型
typedef struct LNode {
ElemType data;
struct LNode *next;
} *Link, *Position;
// 链表类型
typedef struct {
Link head, tail; // 分别指向线性表中的头结点和最后一个结点
int len; // 指示线性表中数据元素的个数
}
// 分配有p指向的值为e的结点,并返回OK;若分配失败,则返回ERROR
Status MakeNode(Link &p, ElemType e);
// 释放p所指结点
void FreeNode(Link &p);
// 构造一个空的线性链表L
Status InitList(LinkList &L);
// 销毁线性链表L,L不再存在
Status DestroyList(LinkList &L);
// 将线性链表L重置为空表,并释放原链接的结点空间
Status ClearList(LinkList &L);
// 已知h指向线性链表的头结点,将s所指结点插入在第一个结点之前
Status InsFirst(Link h, Link s);
// 已知h指向线性链表的头结点,删除链表中的第一个结点并以q返回
Status DelFirst(Link h, Link &q);
// 将指针s所指(彼此以指针相链)的一串结点链接在线性链表L的最后一个结点之后,并改变链表L的尾指针指向新的尾结点
Status Append(LinkList &L, Link s);
// 删除线性链表L中的尾结点并以q返回,改变链表L的尾指针指向新的尾结点
Status Remove(LinkList &L, Link &q);
// 已知p指向线性链表L中的一个结点,将s所指结点插入在p所指结点之前,并修改指针p指向新插入的结点
Status InsBefore(LinkList &L, Link &p, Link s);
// 已知p指向线性链表L中的一个结点,将s所指结点插入在p所指结点之后,并修改指针p指向新插入的结点
Status InsAfter(LinkList &L, Link &p, Link s);
// 已知p指向线性链表中的一个结点,用e更新p所指结点中数据元素的值
Status SetCurElem(Link &p, ElemType e);
// 已知p指向线性链表中的一个结点,返回p所指结点中数据元素的值
Status GetCurElem(Link p);
// 若线性链表L为空表,则返回TRUE,否则返回FALSE
Status ListEmpty(LinkList L);
// 返回线性链表L中元素个数
int ListLength(LinkList L);
// 返回线性链表L中头结点的位置
Position GetHead(LinkList L);
// 返回线性链表L中最后一个结点的位置
Position GetLast(LinkList L);
// 已知p指向线性链表L中的一个结点,返回p所指结点的直接前驱的位置,若无前驱,则返回NULL
Position PriorPos(LinkList L, Link p);
// 已知p指向线性链表L中的一个结点,返回p所指结点的直接后继的位置,若无后继,则返回NULL
Position NextPos(LinkList L, Link p);
// 返回p指示线性链表L中第i个结点的位置,并返回OK,i值不合法时返回ERROR
Status LocatePos(LinkList L, int i, Link &p);
// 返回线性链表L中第1个与e满足函数compare判定关系的元素的位置, 若不存在这样的元素,在返回NULL
Position LocateElem(LinkList L, ElemType e, Status(*compare)(ElemType, ElemType));
// 依次对L的每个元素调用函数visit(), 一旦visit()失败,则操作失败
Status ListTraverse(LinkList L, Status(*visit)());
Status ListInsert_L(LinkList &L, int i, ElemType e) {
// 在带头结点的单链线性表L的第i个元素之前,插入元素e
if (!LocatePos(L, i-1, h)) {
// i值不合法
return ERROR;
}
if (!MakeNode(s, e)) {
// 结点存储分配失败
return ERROR;
}
// 对于从第i个结点开始的链表,第i-1个结点是它的头结点
InsFirst(h, s);
return OK;
}
void MergeList_L(LinkList &La, LinkList &Lb, LinkList &Lc, int (*compare)(ElemType, ElemType)) {
// 已知单链线性表La和Lb的元素按值非递减排列
// 归并La和Lb得到新的单链线性表Lc,Lc的元素也按值非递减排列
if (!InitList(Lc)) {
// 存储空间分配失败
return ERROR;
}
// ha和hb分别指向La和Lb的头结点
ha = GetHead(La);
hb = GetHead(Lb);
// pa和pb分别指向La和Lb中当前结点
pa = NextPos(ha);
pb = NextPos(hb);
// La和Lb均非空
while (!Empty(La) && !Empty(Lb)) {
// a和b为两表中当前比较元素
a = GetCurElem(pa);
b = GetCurElem(pb);
if ((*compare)(a, b) ,= 0) {
// a <= b
DelFirst(ha, q);
Appand(Lc, q);
pa = NextPos(La, pa);
} else {
// a > b
DelFirst(hb, q);
Appand(Lc, q);
pb = NextPos(Lb, pb);
}
}
if (!Empty(La)) {
// 链接La中剩余结点
Appand(Lc, pa);
} else {
// 链接Lb中剩余结点
Appand(Lc, pb);
}
// 释放La和Lb的头结点
FreeNode(ha);
FreeNode(hb);
return OK;
}
链表应用:求解一元多项式及相加
上章内容链接:https://blog.csdn.net/daqino1/article/details/88703321
下章内容链接:https://blog.csdn.net/daqino1/article/details/88832307