数据结构2(第二章)

观看bi站王卓老师的数据结构记的笔记

观看建议:我是在语雀记的笔记,大家可以直接复制之后在语雀打开,就可以很方便的查看,在语雀,图片可以自由扩大缩小,这里不太ok。

第二章

2.1线性表的定义和特点:

☎️定义

☎️线性表的例子

☎️线性表的逻辑特征

线性表是一种典型的线性结构

2.2案例引入

☎️案例2.1 一元多项式的运算

解决:只需要这么3个空间就可以了

0

10000

20000

1

3

2

☎️案例2.2稀疏多项式的运算

表的填入过程:

(1)先对比(7,0)和(8,1),1>0, 所以指数不相同,将指数小的项(7,0)复制到新数组中

(2)对比(3,1)和(8,1),指数都是1,系数3+8,

(3)对比(9,8)和(22,7),指数7<8,(22,7)复制到新数组

(4)(9,8)和(-9,8),8相同,-9+9=0,不用复制进新数组

(5)剩(5,17),直接复制进去新数组

0

1

7

5

7

11

22

17

多项式的相加:

2.3线性表的类型定义

基本操作1

GetElem(l,i,&e);

2.4线性表的顺序表现和实现

2.41线性表的顺序存储表示

顺序存储结构

顺序表中元素存储位置的计算

顺序表的顺序存储表示

多项式的顺序存储结构类型定义

Polynomail是这个元素的类型

图书表的顺序存储结构类型定义

2.5类C语言的有关操作补充

补充:元素类型说明

ElemType:代表线性表当中元素的类型,你这个元素想要什么类型,就可以把他换成什么类型

也可以直接像右上角那样直接定义

补充:数组定义

数组的这个名字data存放的是数组的第一个元素首地址,既然里面存放地址,可以直接定义成一个指针

补充:C语言的动态内存分配

要把开辟出的空间分成几部分就需要看ElemType的类型是什么

(ElemType*)强制转换符

补充:C++的动态内存分配

补充:C++中的参数传递

传值方式:

传的是值,但是实参和形参各用各的空间

传地址方式--指针变量作为参数

形参变化影响实参

m的值存放的是a的地址,指针变量t与 m交换

传地址方式--数组名作参数

传递数组的首元素的地址来传递整个数组

传地址方式--引用类型作参数

注意事项:

2.6线性表的顺序表示和实现

线性表的顺序存储表示

顺序表示意图

elem是数组名

L是指针,可以用L->elem来引用

顺序表基本操作的实现

操作算法中用到的预定义常量和类型

参数用的是引用型,对实参进行操作就是对形参进行操作

InitList_Sq是线性表名字,是顺序存储的,所以是Sq(sequence)

返回的状态值是Status

先动态分配把这个数组分配好,数组当作首元素的基地址赋值给

顺序标基本操作的实现--补充:几个简答操作

顺序表上的查找操作

第i个记录被查找的概率:即1/n

顺序表的插入

执行次数最多的这条语句的执行次数,时间复杂度为O(n)

顺序表的删除

顺序表(线性表的顺序存储结构)的特点

空间复杂度指的是 不需要辅助的空间,在原来的位置就可以操作

顺序表优缺点

克服这一缺点:链表

2.7线性表的链式存储和实现

2.7.1总起:

定义

例子

与链表有关的术语

讨论

顺序存储法的特点

2.7.2单链表的定义和表示

这个指针指向的变量仍然有data和next,就是struct Lode类型

node代表链表中的结点,LinkNode指的是指向这种结点的一个指针,都是类型

2.7.3单链表的基本操作和实现

补充算法一:判断链表是否为空

补充算法2:单链表的销毁

补充算法3:清空链表

补充算法4:求单链表的表长

int ListLength_L (Linktist L){ //返回L中数据元素个数
LinkList p;
p=L->next;                  //p指向第一个结点
  i=0;
while(p) {                 //遍历单链表,统计结点数
  i++;
  p=p->next;
}
return i;
}

知识回顾

算法:取单链表中第i个元素的内容

Status GetElem L(LinkList L, int i, ElemType &e){ //获取线性表L中的某个数据元素的内容,
  //通过变量e返回
  p=L->next; j=1;//初始化
  while(p&&j<i ){ //向后扫描,直到p指向第i个元素或p为空
    p=p->next; ++j;
  }
if(!p || j>i)return ERROR; //第i个元素不存在
  e=p->data;                //取第i个元素
  return OK:
}//GetElem L

算法2.8按值查找

算法2.9插入--在第i个结点前插入值为e的新节点

//在L中第i个元素之前插入数据元素e
Status Listinsert L(LinkList &L,int i,ElemType e){
    p=L; j=0;
while(p &&j<i-1){p=p->next; ++j;}//寻找第i-1个结点,p指向i-1结点
    if(!p lj>i-1) return ERROR;// i大于表长+1或者小于1,插入位置非法
    s=new LNode;s->data=e;//生成新结点s,将结点s的数据域置为e
    s->next=p->next; //将结点s插入L中
    p->next=s;
    return OK;
}//Listinsert L

算法2.10删除--删除第i个结点

//将线性表L中第i个数据元素删除
Status ListDelete L(LinkList &L,int i,ElemType &e){
    p=L;j=0;
    while(p->next &&j<i-1){p=p->next; ++j;}
//寻找第i个结点,并令p指向其前驱
    if(!(p->next)||j>i-1)return ERROR; //删除位置不合理
    q=p->next;//临时保存被删结点的地址以备释放
    p->next=q->next;//改变删除结点前驱结点的指针域
    e=q->data;          //保存删除结点的数据域
    delete q;            //释放删除结点的空间
    return OK;         
}//ListDelete L

单链表的查找、插入、删除算法时间效率分析

Lnode *LocateELem_L(LinkListL, Elemtype e){
    //在线性表L中查找值为e的数据元素
    //找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
    p=L->next;
    while(p &&p->data!=e)
        p=p->next;
     return p;
}

【算法2.11】建立单链表:头插法--元素插入在链表头部,也叫前插法

void CreateList H(LinkList &L,int n){
L=new LNode;
L->next=NULL; //先建立一个带头结点的单链表
for(i=n;i>0;--i){
p=new LNode; //生成新结点 p=(LNode*)malloc(sizeof(LNode));
    cin>>p->data; //输入元素值 scanf(&p-> data);
    p->next=L->next;//插入到表头
    L->next=p;
  }
}//CreateList H

【算法2.12】建立单链表:尾插法--元素插入在链表尾部,也叫尾插法

//【算法描述】 //正位序输入n个元素的值,建立带表头结点的单链衣L
void CreateList R(LinkList &L, int n){
    L=new LNode;L->next=NULL;
r=L;//尾指针r指向头结点
    for(i=0<i++i){
p=new LNode; 
        cin>>p->data;//生成新结点 ,输入元素值
        p->next=NULL;
        r->next=p;//插入到表尾
        r=p;//r指向新的尾结点
    }
}//CreateList_R
//算法的时间复杂度是O(n)

2.7.4循环链表

定义

带尾指针循环链表的合并

2.7.5双向链表

为什么讨论

结构

特点

算法2.13 双向链表的插入

算法2.14 双向链表的删除

2.7.6单链表、循环链表和双向链表的时间效率比较

2.8顺序表和链表的比较

存储密度:

链式存储结构的优缺点

比较

2.9线性表的应用

2.9.1线性表的合并

void union(List &La, List Lb) {
    La_len = ListLength(La);    // 获取La的当前长度
    Lb_len = ListLength(Lb);    // 获取Lb的当前长度
    for(i=1; i<=Lb_len; i++) {  // 遍历Lb中的每个元素
        GetElem(Lb, i, e);      // 获取Lb中第i个元素存入e
        if(!LocateElem(La, e))  // 检查e是否在La中存在
            ListInsert(&La, ++La_len, e); // 如果不存在,插入到La末尾
    }
}

算法:取单链表中第i个元素的内容

Status GetElem L(LinkList L, int i, ElemType &e){ //获取线性表L中的某个数据元素的内容,
  //通过变量e返回
  p=L->next; j=1;//初始化
  while(p&&j<i ){ //向后扫描,直到p指向第i个元素或p为空
    p=p->next; ++j;
  }
if(!p || j>i)return ERROR; //第i个元素不存在
  e=p->data;                //取第i个元素
  return OK:
}//GetElem L

单链表的查找、插入、删除算法时间效率分析

Lnode *LocateELem L(LinkListL, Elemtype e){
    //在线性表L中查找值为e的数据元素
    //找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
    p=L->next;
    while(p &&p->data!=e)
        p=p->next;
     return p;
}

算法2.9插入--在第i个结点前插入值为e的新节点

//在L中第i个元素之前插入数据元素e
Status Listinsert L(LinkList &L,int i,ElemType e){
    p=L; j=0;
while(p &&j<i-1){p=p->next; ++j;}//寻找第i-1个结点,p指向i-1结点
    if(!p lj>i-1) return ERROR;// i大于表长+1或者小于1,插入位置非法
    s=new LNode;s->data=e;//生成新结点s,将结点s的数据域置为e
    s->next=p->next; //将结点s插入L中
    p->next=s;
    return OK;
}//Listinsert L

2.9.2有序表的合并

算法2.16有序表的合并-用顺序表实现

void MergeList_Sq(SqList LA, SqList LB, SqList &LC) {
    // 初始化指针指向两个表的起始位置
    pa = LA.elem;    // pa指向LA的第一个元素
    pb = LB.elem;    // pb指向LB的第一个元素
    
    // 设置合并后新表LC的长度
    LC.length = LA.length + LB.length;  // 新表长度为两表长度之和
    
    // 为合并后的新表分配存储空间
    LC.elem = new ElemType[LC.length];  // 动态分配数组空间
    
    // 初始化指针pc指向新表的第一个位置
    pc = LC.elem;    // pc指向LC的第一个空位
    
    // 计算两个表的末尾指针位置
    pa_last = LA.elem + LA.length - 1;  // pa_last指向LA的最后一个元素
    pb_last = LB.elem + LB.length - 1;  // pb_last指向LB的最后一个元素
    
    while(pa <= pa_last && pb <= pb_last) { // 两个表都非空
    if(*pa <= *pb) *pc++ = *pa++; // LA当前元素较小,放入LC
    else *pc++ = *pb++;           // LB当前元素较小,放入LC
        //移动取出元素的表的指针(pa或pb)和pc指针

     while(pa <= pa_last) *pc++ = *pa++; 
    // LB表已到达表尾,将LA中剩余元素加入LC
    
    while(pb <= pb_last) *pc++ = *pb++; 
    // LA表已到达表尾,将LB中剩余元素加入LC
}//MergeList sq

最后一个元素的地址 = 基地址 + (表长度 - 1) × 元素大小,指针运算会自动考虑数据类型大小

算法2.17有序表的合并-用链表实现

过程

算法

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;   // 插入剩余段
    delete Lb;    // 释放Lb的头结点
}

空间复杂度是O(1),一直在改变指针的指向而已

2.10案例分析与实现

【案例2.1】一元多项式的运算:实现两个多项式加、减、乘运算

案例2.2:稀疏多项式的运算

多项式创建

1️⃣2️⃣3️⃣4️⃣

1️⃣多项式创建---算法步骤

2️⃣多项式创建---【算法描述】
void CreatePolyn(Polynomial &P, int n) {
    // 输入m项的系数和指数,建立表示多项式的有序链表
    P = new PNode;
    P->next = NULL;    // 先建立一个带头结点的单链表
    
    for(i = 1; i <= n; ++i) {
        s = new PNode;    // 生成新结点
        cin >> 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和其前驱结点pre之间
        pre->next = s;
    }
}

多项式相加

1️⃣多项式相加---【算法分析】

2️⃣多项式相加---算法步骤

案例2.3 图书信息管理系统

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值