数据结构
- 第零章 引入
- 第一章绪论
- 第二章 线性表
- 第三章 栈和队列
- 第四章 串、数组和广义表
- 第五章 树和二叉树
- 第六章 图
- 第七章 查找
- 第八章 排序
本篇是关于王卓老师+王道咸鱼的数据结构学习笔记。
递归思想:依次往下调用(这个调用是调用的自身函数),调用到最后一层时得到的是最里层(即最下层)的数据,然后一层一层往上代(相当于嵌套函数),即可得到最终结果。
第零章 引入
0.1 元素类型说明
【对应2.4.1——线性表的顺序存储表示】
ElemType是元素类型。若顺序表中存放的是ABCD,则可写为char;存放的是多项式系数,则写为float。
也可以单独提出来先定义。如typedef char ElemType;
0.2 数组定义
- 数组静态分配:数组的名字中存放的是数组中的首元素data[0]的地址,即此数组的基地址。
- 数组动态分配:*data形式上是指针变量,但是它也可以表示一个数组,可以用它来存放数组中第一个元素的地址。
0.3 内存动态分配
- malloc()的参数要求是整数,即这个分配的这块空间的字节数。
- MaxSize是根据需要自定义的。
- ElemType决定了动态申请的空间怎样去划分。若是char类型,则是1字节1个空间;若是int类型,则是4字节1空间。。* 代表分配好的这个结果是一个指针。。外面的()表示强制类型转换。
- (ElemType*)表示指向数据元素的指针。
- L.data即获得了数组中这一大片空间的基地址。
- free()中的参数要求是一个指针变量。
0.4 参数传递
swap函数中最后m、n释放了。因此最终结果a、b并没有得到改变。
*m改变的是其中的内容。因此a、b值互换。
交换的是m、n的地址,对a、b没有影响。
- 注意sub()中b[]方括号中不可以指明大小
- 最终结果相当于重新给a赋值。最终输出world
也可以理解为j是一个地址。i和j共同使用同一个空间(即地址相同)。
对m、n操作实际上就是对a、b操作。因此最后a、b值成功互换。
0.5 操作算法中用到的预定义常量和类型
0.6 结构类型的比较
第一章绪论
1.1 数据结构的研究内容
数据结构就是操作对象及操作对象之间的关系。
早期,计算机主要用于数值计算。
现在,计算机越来越多地用于非数值计算。
❀内容小结
1.2 基本概念和术语
1.2.1 数据、数据元素、数据项、数据对象
- 数据是计算机程序加工的原料。
- 数值型数据通常是指可以加减乘除、平方开方等的算数运算的。
- 对于非数值型问题,通常会关心 每个个体的具体信息 以及 每个个体之间的关系。
1.2.2 数据结构
- 同一个数据对象里的数据元素,可以组成不同的数据结构。
- 不同的数据元素,可以组成相同的数据结构。
1.2.2.1 逻辑结构
1.2.2.2 存储结构
例中字符串数组是有先后顺序的。(此顺序用内存中的存储位置来表示——在前面的元素存储时存在前面,即按照元素的位置存储)
指针即地址。
通讯录便是常见的索引存储结构。
可与《查找》一章联系学习。
1.2.3 数据类型和抽象数据类型
(1)数据类型
(2)抽象数据类型
定义一个ADT,就是在“定义”一种数据结构。
【确定了ADT的存储结构,才能“实现”这种数据结构】
基本操作名(参数表):
- 如求圆的面积,其中半径为r。则记为 area® 。。【这种便是赋值参数】
- 如求乘方,x的y次方。则记为power(x,y)
- 如对一个图像G进行放大n倍的操作。则记为scale(&G,n)。。【注意:加上&则表示最后返回G作为结果,这种便是引用型参数。】
复数构造即加减的具体实现:
#include<stdio.h>
typedef struct {
float realpart; //定义实部
float imagpart; //定义虚部
}Complex;
//构造复数
Complex assign(float real,float imag) {
Complex c;
c.realpart = real;
c.imagpart = imag;
return c;
}
//两复数求和
Complex add(Complex A,Complex B){
Complex c;
c.realpart = A.realpart + B.realpart;
c.imagpart = A.imagpart + B.imagpart;
return c;
}
//两复数求差
Complex minus(Complex A, Complex B) {
Complex c;
c.realpart = A.realpart - B.realpart;
c.imagpart = A.imagpart - B.imagpart;
return c;
}
int main()
{
Complex b,c,d,e;
b = assign(2.0, 4.0);
c = assign(1.0, 1.0);
d = add(b, c);
e = minus(b, c);
printf("实部为2虚部为4的复数是:b=%f+%f i\n", b.realpart, b.imagpart);
printf("实部为1虚部为1的复数是:c=%f+%f i\n", c.realpart, c.imagpart);
printf("两复数相加是:d=%f+%f i\n", d.realpart, d.imagpart);
printf("两复数相减是:d=%f+%f i\n", e.realpart, e.imagpart);
return 0;
}
❀内容小结
1.3 抽象数据类型的表示与实现
main()函数部分:
typedef用于声明一个已有的数据类型的新名字。
1.4 算法和算法分析
算法是求解问题的步骤。
- 输入取自于某个特定的对象的集合。
- 输出是与输入有着某种特定关系的量。
矛盾的意思是有时候为了节省时间可能会消耗空间,有时为了节省空间可能会花费更多的时间。【因此需要综合平衡】
1.4.1 算法时间效率的度量
【 更多的是选择事前分析法】。
- 事后统计法存在的问题:
若假设每条语句所需的时间均为单位时间,则可以暂时不考虑。
- 行一 是从1到n,循环变量执行n次。但是还需要最后执行一次来判断,即条件不成立则退出循环。因此执行n+1次。
- 行二 是外层循环的循坏体,循环体要执行n次;每执行1次这句语句,都要执行n+1次。因此执行n(n+1)次。
- 行三 是中层循环的循环体,外层循环每执行一次,中层循环执行n次,而外层循环执行了n次。因此执行了n*n次。
1.4.2 时间复杂度
由例可知:很多算法执行时间与输入的数据有关。
一般只考虑最坏和平均。
口诀:【常对幂指阶】
例题:
也可直接写成O(lgn)。即不用去考虑底数具体是多少。
1.4.3 空间复杂度
❀内容小结
第二章 线性表
2.1 线性表的定义和特点
单位历年中,第一年6,第二年17…以此构成前后结点的线性关系。
2.2 案例引入+分析与实现
2.2.1 一元多项式的运算
把每一项的系数拿出来存成一个线性表。
系数的下标 i 还可隐含地表示指数。
2.2.2 稀疏多项式
如相加:
线性表A中有4项,B中有3项。
- 假设每项指数均不同,则最多可得7项。
- 极端情况下每一项刚好指数一样,系数是相反数,则最少可得0项。
2.2.3 图书信息管理系统
存储方式:
❀内容小结
2.3 线性的类型定义
这里指的类型实际上就是抽象数据类型。
数据对象就是一些元素,元素的个数n>=0;
<ai-1,ai>序偶关系,ai-1是ai的前驱。
2.4 线性表的顺序表示和实现
2.4.1 线性表的顺序存储表示
(1)顺序表的定义
元素之间的关系由存储单元的邻接关系来体现。
找地址是O(1)
数组长度定义模板
(2)顺序表的实现
I.静态分配
【顺序表的实现示例代码】
ps:为给数据元素设置默认值
【初始化时,设置初始值是必须做的】
初始就分配很大的内存空间是不可行的。
II.动态分配
ps:最终如下
例:
❀内容小结
左边框是静态,右边框是动态。
2.4.2 顺序表基本操作的实现
画线的较难。
ps:加上&
(1)线性表初始化
- 行1:&L即通过引用型变量将构造好了的顺序表L返回。
- 若成功构造则是为变量L分配空间。
- 行2:动态分配,获得这个数组中的基地址。
- 行4:若成功分配,注意此时里面没有元素,因此长度为0。
- 行3:异常处理。OVERFLOW是溢出的意思,返回的是-2。
(2)销毁线性表
注意:销毁的前提条件是该线性表存在。
(3)清空线性表
length代表线性表元素的个数,将其令为0。则表示线性表内无元素,为空。
(4)求线性表长度
(5)判断是否为空
返回1表示为空,返回0表不为空。
(6)取值
算法的时间复杂度为O(1)。
(7)查找
按位查找
【1.静态分配】
【2.动态分配】
按值查找
这是进行顺序查找的算法。
也可以写成如下:
详见0.6 结构类型的比较
(8)插入
注意:
- 如果有n个元素,那么插入的位置只能是0~n+1位置上。
- 如果线性表容量已经放满,还要插入数据的话,会存在溢出情况。
【综上,需要首先判断插入位置i是否合法以及存储空间是否已满】
插入中的移动方式:先后面的元素往后移,再前面的元素往后移
以下是王卓老师的算法
算法时间主要耗费在移动元素的操作上。
(9)删除
第2步是可以省略的。即直接删除。
删除中的移动方式:先前面的元素往前移,再后面的元素往前移
算法时间主要耗费在移动元素的操作上。
❀ 内容小结
2.5 线性表的链式表示和实现
❀内容小结
2.5.1 单链表的定义和表示