观看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;
}
}