《数据结构与算法》笔记(2补充)

本文介绍了循环链表的概念、特点以及合并操作,随后讨论了双向链表的存储结构、插入和删除算法,并比较了链式存储结构的优缺点。此外,文章还提供了线性表的合并算法,包括顺序表和链表的合并,以及一元多项式的加法运算实现。
摘要由CSDN通过智能技术生成

参考课程:数据结构与算法基础(青岛大学-王卓),笔记中截图都来自王老师的课件
参考书目:《数据结构(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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值