参考课程:数据结构与算法基础(青岛大学-王卓),笔记中截图都来自王老师的课件
参考书目:《数据结构(C语言版)》严蔚敏 吴伟民 著
循环链表
循环链表是一种头尾相接的链表,即表中最后一个结点的指针域指向头结点
,整个链表形成一个环
。
优点:从表中任一结点出发均可以找到表中其他结点。
注意:由于循环链表中没有NULL指针,故涉及遍历操作时,其种植条件不像非循环链表判断p或p->next是否为空,而是判断它们是否等于头指针。
常常使用带尾指针的循环链表,方便读取收尾结点。
【例】将两个带有尾指针的循环链表进行合并:
LinkList_ Connect(LinkList Ta,LinkList Tb){
p=Ta->next;
Ta->next=Tb->next->next;
free(Tb->next);
Tb->next=p;
return Tb;
}
双向链表
在单链表的每个结点里再增加一个指向其直接前驱的指针域prior,这样链表中就形成了有两个方向不同的链,故称为双向链表。
// ------双向链表的存储结构------
typedef struct DuLNode{
ElemType data;
struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;
和单循环链表类似,双向链表也可以有循环表。双向链表的ListLength和GetLength操作和线性链表操作几乎相同。插入和删除操作需要都需要先进行查找操作(查找操作的时间复杂度为O(n)),两者时间复杂度都是O(n)。
【算法2.12】双向链表的插入
Status ListInsert_DuL(DuLinkList &L,int i,Elemtype e){
if (!(p=GetElemP_DuL(L,i))) return ERROR; // 先找到第i个结点
s=(DuLinkList)malloc(sizeof(DuLNode));
s->data=e;
s->prior=p->prior;
p->prior-?next=s;
s->next=p;
p->prior=s;
return OK;
}
【算法2.13】双向链表的删除
Status ListDelet_DuL(DuLinkList &L,int i,ElemType &e){
if (!(p=GetElemP_DuL(L,i))) return ERROR;
e = p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
free(p);
return OK;
}
循序表和链表的比较
链式存储结构的优点:
- 结点空间可以动态申请和释放
- 数据元素的逻辑次序靠结点的指针来指示,插入和删除时不需要移动数据元素
链式存储结构的缺点:
-
存储密度小, 存储密度 = 结点数据本身占用的空间 结点占用的空间总量 存储密度=\frac{结点数据本身占用的空间}{结点占用的空间总量} 存储密度=结点占用的空间总量结点数据本身占用的空间
链式存储结构每个结点的指针域需额外占用存储空间,当每个结点的数据域所占字节不多时,指针域所占存储空间的比重显得很大
-
链式存储结构是非随机存取结构,对任一结点的操作都要从头指针依指针链查找到该节点,增加了算法的复杂度
线性表的应用
线性表的合并
【算法2.14】
void union(List &La,List &Lb){
La_len=ListLength(La);
Lb_len=ListLength(Lb);
for (i=1;i<Lb_len;i++){
GetElem(Lb,i,e);
if (!LocateElem(La,e)){
ListInsert(&La,++La_len,e)
}
}
}
时间复杂度为 O ( n 2 ) O(n^2) O(n2)
有序表的合并(顺序表)
(1)创建一个空表Lc(2)依次从La或Lb中“摘取”元素值较小的结点插入到Lc表的最后,直至其中一个表变空为止(3)继续将La或Lb其中一个表的剩余结点插入在Lc表的最后
【算法2.15】
void MergeList_Sq(SqList LA,SqList LB,SqList &LC){
pa=LA.elem;
pb=LB.elem;
LC.length=LA.length+LB.length;
LC.elem=(ElemType*)malloc(LC.length*sizeof(ElemType));
pc=LC.elem;
pa_last=LA.elem+LA.length-1; // 指向LA中最后一个元素
pb_last=LB.elem+LB.length-1; // 指向LB中最后一个元素
while (pa<=pa_last && pb<=pb_last){
if (*pa <= *pb) *pc++=*pa++;
else *pc++=*pb++;
}
while (pa<=pa_last) *pc++=*pa++;
while (pb<=pb_last) *pc++=*pb++;
}
算法的时间复杂度和空间复杂度都为O(ListLength(La)+ListLength(Lb))
有序表的合并(链表)
【算法2.16】
void MergeList_L(LinkList &La,LinkList &Lb,LinkList &Lc){
pa=La->next;
pb=Lb->next;
pc=Lc=La; // 用La的头结点作为Lc的头结点
while (pa && pb){
if (pa->data<=pb->data){
pc->next=pa;
pc=pa;
pa=pa->next;
}
else{
pc->next=pb;
pc=pb;
pb=pb->next;
}
}
pc=>next=pa ? pa : pb;
/* 相当于
if(pa==NULL) pc->next=pb;
else pc->next=pa;*/
free(Lb);
}
时间复杂度为O(ListLength(La)+ListLength(Lb)),空间复杂度是O(1)。
案例分析与实现
【案例】一元多项式的运算:实现两个多项式的加、减、乘运算。
#include <stdio.h>
#include <stdlib.h>
typedef struct PNode {
float coef; //系数
int expn; //指数
struct PNode* next;
}PNode, *Polynomial;
Polynomial InitList();
void CreatePolyn(Polynomial P, int n);
void AddPolyn(Polynomial P1, Polynomial P2);
// 初始化创建头结点
Polynomial InitList(){
Polynomial P=(Polynomial)malloc(sizeof(PNode));
P->next = NULL;
P->coef = 0;
P->expn = 0;
return P;
}
// 创建多项式
void CreatePolyn(Polynomial P, int n) {
PNode *s, *pre, *q;
for (int i = 1; i <= n; i++) {
s = (PNode*)malloc(sizeof(PNode));
printf("依次输入系数和指数,空格隔开");
scanf("%f %d", &s->coef, &s->expn);
pre = P; // pre用于保存q的前驱,初始值为头结点
q = P->next; // q初始化,指向首元结点
while (q && (q->expn < s->expn)) {
pre = q;
q = q->next;
}
s->next = q; // 将输入项s插入到q和q的前驱结点pre之间
pre->next = s;
}
}
// 打印多项式
void PrintPolyn(Polynomial P) {
PNode* temp;
temp = P->next;
while (temp) {
if (temp->coef > 0) printf("+%.2fX^%d", temp->coef, temp->expn);
else printf("%.2fX^%d", temp->coef, temp->expn);
temp = temp->next;
}
printf("\n");
}
// 多项式求和
void AddPolyn(Polynomial P1, Polynomial P2) {
PNode* p1,*p2,*p3;
p1 = P1->next;
p2 = P2->next;
p3 = P1;
while (p1 && p2) {
if (p1->expn < p2->expn) {
p3->next = p1;
p3 = p1;
p1 = p1->next;
}
else if (p1->expn > p2->expn) {
p3->next = p2;
p3 = p2;
p2 = p2->next;
}
else if (p1->expn == p2->expn) {
p1->coef = p1->coef + p2->coef;
if (p1->coef == 0) {
p1 = p1->next;
p2 = p2->next;
p3->next = p1;
}
}
}
p3->next = p1 ? p1 : p2;
}
int main() {
int n1, n2;
Polynomial P1, P2;
P1 = InitList();
P2 = InitList();
printf("=====1.创建两个多项式=====\n");
printf("输入第一个多项式项数:");
scanf("%d",&n1);
CreatePolyn(P1, n1);
printf("输入第二个多项式项数:");
scanf("%d", &n2);
CreatePolyn(P2, n2);
printf("=====2.多项式预览=====\n");
printf("多项式1为:");
PrintPolyn(P1);
printf("多项式2为:");
PrintPolyn(P2);
printf("=====3.多项式加法=====\n");
AddPolyn(P1, P2);
printf("相加的结果为:");
PrintPolyn(P1);
return 0;
}