4.1 引子
1、查找
1)定义:给定关键字K,从集合R中找出关键字与K相同的记录的过程。
2)分类
(1)静态查找:集合中的记录是固定的,不涉及对记录的插入和删除操作,而是仅仅按关键字查找记录。通常是从一个线性表查找数据元素。
①方法:
a、顺序查找(从最后一个元素往回判别)【时间复杂度O(n)】
·有哨兵(碰到要退出循环)
·无哨兵(每次要判断是否到下界)
b、二分查找法【时间复杂度O(logN)】
·前提:元素的关键字满足有序且连续存放(数组)
·算法思想:
先判断K与mid,若K=mid,返回mid;若K>mid,mid移到后半段中间,left=mid+1;若K<mid,mid
移到左半段中间,right=mid-1。(中间值向下取整)
(2)动态查找:集合中的记录是动态变化的,即记录可能是要发生插入和删除操作。
3)手段:①比较 ②映射
4)效率:平均查找长度ASL
4.2 树
1、定义:n个结点构成的有限集合。
性质 :①根,r 。 ②其余结点可分为m个互不相交的有限集,即子树。③ 除根结点,每个结点有且仅有一个父结点。④一棵N个结点的树有N-1条边。
术语:①结点的度:结点的子树个数。
②树的度:树的所有结点中最大的度数。
③叶结点:度为0的结点。
④父结点:有子树的结点是其子树的根结点的父结点。
⑤子节点\孩子结点:若A是B的父结点,则B是A的子结点。
⑥兄弟结点:同个父结点的各个子结点是兄弟结点。
⑦路径和路径长度:路劲所包含的边的个数为路径长度。
⑧祖先结点:沿树根到某一结点路径上的所有结点。
⑨子孙结点:某一结点上的子树中的所有结点。
⑩结点的层次:根结点在1层,其它任一结点的层数是其父结点的层数加1。
⑪树的深度:所有结点中的最大层次。
2、表示
1)数组实现:较难
2)链表实现:每个结点难以统一格式或者统一格式造成空间浪费。
3)儿子兄弟表示法:
引申——> 二叉树:
4.3、二叉树
1、定义和性质
1)定义:一个有穷的结点集合。若不空,则由根结点和左子树和右子树
组成。
2)重要性质
①第i层的最大结点数是:
②深度为K的二叉树有最大结点总数为:
③对任何非空二叉树T,n0表示叶结点,n2是度为2的结点,则有:n0 = n2 + 1。
3)特殊二叉树
①斜二叉树
②完美二叉树\满二叉树
③完全二叉树:上->下,左->右编号,编号i结点与满二叉树编号i的结点在二叉树中位置相同。(即最后一层可从右往左按顺序缺叶结点)
2、存储结构
1)顺序存储结构
①完全二叉树:上->下,左->右顺序存储n个结点(数组)
·非根结点i的父结点的序号是i/2(i是序号)
·结点i的左孩子是2i,右孩子是2i-1
②一般二叉树:补全为完全二叉树,但是会造成空间浪费
2)链表存储
3)静态链表【物理数组,链表思想】
left、right表示数组的下标,空时为-1(Null)。【NULL=0】
3、操作集
1)判断BT是否为空【BT∈二叉树】
2)遍历,按某顺序访问每个结点。
(1)先序——根、左子树、右子树
(2)中序——左子树、根、右子树
(3)后序——左子树、右子树、根
·先序(①)、中序(②)、后序(③)遍历过程中经过结点的路线一样,只是访问各结点的时机不同,分别在第1、2、3次碰到时输出。
·非递归遍历——使用堆栈
中序:①遇到一个结点,先压栈
②左子树遍历结束后,从栈顶弹出这个结点并访问它
③按其右指针再去中序遍历右子树
先序:①遇到一个结点,直接访问
②遍历左子树
③遍历右子树
后序:①遇到一个结点,先压栈
②遍历左子树
③遍历右子树,弹出栈顶的结点并访问它
(4)层次遍历——从上到下、从左到右
①核心问题:二维结构的线性化(需要一个存储结构保存暂时不访问的结点)
②实现:
a、队列实现:根结点入队,然后执行循环:结点出队,访问该结点,其左右儿子入队。
3)创建一个二叉树
4、应用
1)二元运算表达式及其遍历
tips:中序遍历得到的中缀表达式会受到运算符优先级的影响——>输出左子树前先输出左括号,输完再加右括号。
2)由两种遍历序列确定二叉树:其一必须是中序遍历才可以。
3)同构
①概念:T1可以通过若干次左右孩子互换变成T2,则这两棵树是“同构”的。
②判别:判别结点的子树是否一样。
4.4 二叉搜索树\二叉排序树\二叉查找树(BST)
1、性质
1)左子树的所有键值小于其根结点的键值。
2)右子树的所有键值大于其根结点的键值。
3)左右子树都是二叉搜索树。
2、操作
1) Find 【查找的效率决定于树的高度】
和根结点相比,大了到右子树去找,小了到左子树去找,相等直接输出。
2)找最大值和最小值
最小值——最左叶结点 最大值——最右叶结点
3)插入
先找到要插入的位置(类似于查找),但是要保存好指针。
4)删除
①叶结点:直接删除,修改其父结点指针设为NULL。
②只有一个孩子:将其父结点的指针指向要删除结点的孩子结点。
③有左、右两棵子树:用另一结点替代被删除结点:右子树的最小元素或者左子树的最大元素。
3、平衡二叉树(AVL树)
1)概念: 空树或者任一结点左、右子树高度差的绝对值不超过1,即|BF(T)|=<1(BF(T)=hL-HR)。
2)nh 高度为h的平衡二叉树的最少结点树:
3)平衡二叉树的调整
1)RR插入,RR旋转(右单旋)
2)LL插入,LL旋转(左单旋)
4.5 堆
1、概念
优先队列:取出元素的顺序是按照元素的优先权(关键字)大小。
2、实现方式
1)数组
插入——元素总是插入尾部 ~O(1)
删除——查找最大(或最小)关键字 ~O(n)
从数组中删去需要移动元素 ~O(n)
2)链表
插入——元素总是插入链表的头部 ~O(1)
删除——查找最大(或最小)关键字 ~O(n)
删去结点 ~O(1)
3)有序数组
插入——找到合适的位置 ~O(n) 或O()
移动元素并插入 ~O(n)
删除——删去最后一个元素 ~O(1)
4)有序链表
插入——找到合适的位置 ~O(n)
移动元素并插入 ~O(1)
删除——删去最后一个元素 ~O(1)
5)完全二叉树
3、特性
1)结构性:用数组表示的完全二叉树
2)有序性:任一结点的关键字是其子树所有结点的最大值(或最小值)【ps:从根结点到任意结点路径上结点序列的有序性】
①最大堆\大顶堆:最大值
②最小堆\小顶堆:最小值
4、操作
1)插入(最大堆):将新增结点插入到从其父结点到根结点的有序序列中。【O()】
将i指向插入后堆的最后一个元素的位置,向下过滤结点,将item插入。
2)删除:取出根结点元素,同时删除堆的一个结点。【O()】
用最大堆中最后一个元素从根结点开始向上过滤下层结点。
3)最大堆的建立:将已经存在的N个元素按最大堆的要求存放在一个一维数组中。
1)方法1:通过插入操作,将N个元素一个个相继插入到一个初始为空的堆去。【】
2)方法2:在线性时间复杂度下建立最大堆。【O(N)——各个结点高度之和】
①将N个元素按输入顺序存入,先满足完全二叉树的结构特性
②调整各结点位置,以满足最大堆的有序特性
4.6 哈夫曼树与哈夫曼编码
1、定义:最优二叉树\哈夫曼树:WPL最小的二叉树
带权路径长度(WPL):设二叉树有n个叶子结点,每个叶子结点带权值wk,从根结点到每个叶子结点的长度为lk,则每个叶子结点的带权路径长度之和就是:。
2、构造:每次把权值最小的两棵二叉树合并。【】
3、特点
①没有度为1的结点
②n个结点的哈夫曼树共有2n-1个结点
③哈夫曼树的任意非叶结点的左右子树交换后仍是哈夫曼树。
④对同一组权值,可能构造成两棵不同构的哈夫曼树。
4、哈夫曼编码
1)前缀码:任何字符的编码都不是另一个字符编码的前缀——可以无二义地编码
2)二叉树用于编码——>用哈夫曼树能的代价最小的哈夫曼码
①左右分支:0、1
②字符只在叶结点上
4.7 集合的表示及查找
1、表示
1)树结构:每个结点代表一个集合元素【用双亲表示法:孩子指向双亲】
(1)采用数组存储:如下图,parent表示父亲的下标,-1表示是父结点。
(2)查找某个元素所在的集合(用根结点表示)
(3)集合的并运算(为了查找性能,可以采用小的集合合并到相对大的集合中——将父结点的parent改成-n,n为集合的元素个数)
①分别找到X1和X2元素所在集合树的根结点
②不同根,则将其中一个根结点的父结点指针设置成另一个根结点的数组下标。