数据结构--郝斌--笔记
1. 数据结构概念
程序 = 数据结构 + 算法
数据结构是将现实或者其他需要的信息虚拟化到计算机中,以一种特定的格式存储起来(内存)
然后通过算法执行存储起来的数据
程序 = 存储的数据 + 对数据的操作 + 计算机语言
地址:内存编号:从0开始非负整数,范围:0 - FFFFFFFF(0 - 4g-1)
数据结构:
狭义:
数据结构是专门研究数据存储的问题
数据的存储包括两方面:个体的存储 + 个体关系的存储
广义:
数据结构既包含数据的存储也包含数据的操作
对存储数据的操作就是算法
算法:
狭义:
算法是和数据的存储方式密切相关
广义:
算法和数据的存储方式无关--泛型思想(模版、运算符重载、指针)
数据的存储结构有几种
线性
- 连续存储 数组
优点:存取速度快
缺点:插入删除很慢;空间通常有限制;事先需要知道数组长度;需要大块连续内存块 - 离散存储 链表
优点:空间没有限制;插入删除元素很快
缺点:存取速度很慢 - 线性结构的应用:栈、队列
非线性
树
图
详细内容
指针:
- 定义
指针就是地址,地址就是指针
指针变量是存放内存单元地址的变量
指针的本质是一个操作受限的非负整数
int i = 10;
int *p = &i; // 等价于 int *p; p = &i;
p存放了i的地址,所以我们说p指向了i
p和i是完全不同的两个变量,修改其中任意一个变量的值不影响另一个
p指向i,*p就是i变量本身。 - 分类:
基本类型的指针
指针和数组的关系
2.2.1. 指针和一位数组的关系: ①一维数组名指向第一个元素的地址,它的值不能被改变;②下标和指针的关系:a[i] 《====》(a+i).
int p;
p + i: p+i(p所指向的向量所占的字节数)
p - i: p-i(p所指向的向量所占的字节数)
指针变量统一只占4个字节
1.2 结构体
struct Student st = {1000, “zhangsan”, 20}
struct Student *pst;
- st.sid = 99;
- pst -> sid = 99;(pst所指向的结构体变量中的sid这个成员)
注意事项:
a. 结构体变量不能加减乘除,可以相互赋值;
b. 普通结构体变量和结构体指针变量作为函数传参的问题;
1.3 动态内存分配:
使用了malloc方法的是动态分配,否则是静态分配。
malloc方法分配的内存必须用free方法释放内存,否则方法执行完内存也不会释放,除非整个程序执行完毕。
复习
数据结构 = 个体的存储 + 个体的关系存储
算法 = 对存储数据的操作
衡量算法:
时间复杂度、空间复杂度、难易程度、健壮性
2. 数据结构
2.1线性结构
- 概念:可以把所有节点用一根线串联起来
- 分类:连续存储(数组)、离散存储(链表)
常见应用:
栈
- 定义:
一种可以实现“先进后出”的存储结构;类似与箱子 - 分类:
静态栈(数组)
动态栈(链表) - 栈的算法:
出栈
压栈 - 栈的应用:
函数调用
中断
表达式求值
内存分配
缓冲处理
迷宫
队列
- 定义:一种可以事先“先进先出”的存储结构
- 分类
a. 链式队列(链表实现):
b. 静态队列(数组实现): - 性质:
- 通常是循环队列(假如不可循环,随着front和rear的移动,到了最后之前释放的位置不可以再使用)
- 循环队列需要2个参数确定:
-2个参数不同场合有不同含义
①队列初始化:front和rear的值都是0
②队列非空:front代表的队列的第一个元素;rear代表的队列最后一个有效元素的下一个
③队列空:front和rear的值相等不一定为0 - 入队–两步完成:将值存入r所代表位置;(错误写法r=r+1)正确写法:r=(r+1)%数组长度
- 出队–f=(f+1)%数组长度
- 判空:f=r
- 盘满:两种方式:①增加一个标识参数②少用一个元素
预备知识:front的值可能比rear大,也有可能比其小,也有可能相等
(r+1)%数组长度==f :(r和f紧挨着,则队列已满)
队列的具体应用
- 所有和时间有关的操作都和队列有关
2.1.1 数组
- 定义:元素类型相同,大小相等
- 优缺点:
2.1.2 链表(离散存储)
- 定义:n个节点离散分配;彼此通过指针相连;每个节点只有一个后续节点;首节点没有前驱节点;尾节点没有后续节点。
专业术语:
首节点:第一个有效节点
尾节点:最后一个有效节点
头结点:头结点的数据类型和首节点类型一样;第一个有效节点之前的节点;头结点并不存放有效数据;加头节点的目的是为了方便对链表的操作
头指针:指向头结点的指针变量
尾指针:只想尾节点的指针变量
如果我们希望通过一个函数对链表进行处理,我们至少需要接收链表哪些参数–确定一个链表需要几个参数:只需要一个参数头指针,通过头指针可以推算出链表其他所有信息 - 分类:
单链表
双链表:每一个节点有两个指针域
循环链表:能通过任何一个节点找到其他所有节点
非循环链表 - 算法:
遍历
查找
清空
销毁
求长度
排序
删除节点 - 链表的优缺点:
2.2非线性结构
算法:
狭义的算法是与数据的存储方式密切相关的
广义的算法是与数据的存储方式无关的
泛型:利用某种技术达到的效果就是–不同的存储方式,执行的操作一样。
算法解析:
流程(步骤) --》 每个语句功能 --》试数
栈
局部变量实在栈中分配
动态分配的在堆中分配
3. 递归
- 定义:一个函数自己直接或间接调用自己
- 汉诺塔
- 走迷宫
4. 树
4.1 定义
- 有且仅有一个称为根的节点
- 有若干互不相交的子树,这些子树本身也是一棵树
通俗的定义:
1.树是由节点和边组成
2.每一个节点只有一个父节点,但可以有多个子节点
3.但有一个节点例外,该节点没有父节点,此节点成为根节点
专业术语:
1.节点:
2.父节点:
3.子节点:
4.深度:从根节点到最底层节点的层数称之为层数;根节点是第一层
5.叶子节点:没有子节点的节点
6.非终端节点:实际就是非叶子节点
7.度:子节点的个数成为度
4.2 树的分类
- 一般树:任意一个节点的子节点的个数都不受限制
- 二叉树:任意一个节点的子节点个数最多是2个,且子节点的位置不可更改
分类:
2.1 一般二叉树:
2.2 满二叉树:在不增加树的层数的前提下,无法再添加一个节点的二叉树,就是满二叉树
2.3 完全二叉树:如果只是删除了满二叉树最底层、最右边的连续若干个节点,这样形成的二叉树就是满二叉树 - 森林:n个互不相交的树的集合
树的存储
二叉树的存储
- 连续存储(完全二叉树)
优点:查找某个节点的父节点和子节点(也包括判断是否有子节点)速度很快
缺点:耗用内存空间过大 - 链式存储
一般树的存储
双亲表示法(存父节点下标)
- 求父节点方便
孩子表示法
- 求子节点方便(存子节点下标)
双亲孩子表示法(存父节点和子节点下标)
- 求父节点和子节点都很方便
二叉树表示法
- 把一个普通树转化成二叉树存储
- 具体转换方法:
设法保证任意一个节点的左指针域指向它的第一个孩子,右指针域只向它的下一个兄弟。只要能满足此条件,就可以把一个普通树转化成二叉树 - 一个普通树转化成的二叉树一定没有右子树
森林的存储
先把森林转化为二叉树,再存储
把其他树当作他的兄弟,右指针域指向兄弟节点,左指针域指向孩子节点
4.3 操作
遍历
- 先序遍历
先访问根节点,再先序访问左子树,再先序访问右子树 - 中序遍历
中序遍历左子树,再访问根节点,再中序遍历右子树 - 后序遍历(最后访问根节点)
中序遍历左子树,再中序遍历右子树,再访问根节点
已知两种遍历序列求原始二叉树
通过先序和中序或者中序和后序我们可以还原出原始的二叉树
但是通过先序和后续是无法还原出原始的二叉树的
既
只有通过先序和中序,或者中序和后序,我们才可以唯一确定一个二叉树
- 已知先序和中序求后序
例1:
先序:ABCDEFGH
中序:BDCEAFHG
求后序:
①先序可知A为根节点,结合中序可知BDCE为左子树,FHG为右子树
②先序为根,所以B为根,结合中序DCE(先序)为B右子树,(先序CDE)C为根,D为左E为右
③查看右子树,根据先序和中序右子树F为根(没有左子树),G为右子树,H为左子树
后序:DECBHGFA
例2:
先序:ABDGHCEFI
中序:GDHBAECIF
后序:GHDBEIFCA
- 已知中序和后序求先序
中序:BDCEAFHG
后序:DECBHGFA
求先序:ABCDEFGH
4.4 树的应用
树是数据库中数据组织一种重要形式
操作系统子父进程的关系本身就是一棵树
面向对象语言中类的继承关系本身就是一棵树
赫夫曼树
例3:
先:ABCDEFGLH
中:CDBAFLGEH
后:DCBLGFHEA
例3
先:ABFDCLMN
中:BFACDLNM
后:FBCNMLDA