做题笔记
1.顺序查找
不管线性表无序还是有序,成功查找第一个元素比较1次,成功查找第二个元素比较2次,所以每个元素的比较次数只与其位置有关。
简记:**顺序查找与顺序无关**
2.折半查找与二叉排序树
折半查找的性能可用二叉排序树来衡量,平均查找长度和最大查找长度都为O(log n);
二叉排序树的查找性能与数据的输入顺序有关,最好情况下平均查找长度与折半查找相同,但最坏情况形成单支数,查找长度为O(n)。 简记:两者最坏情况不同
3.具有12个关键字的有序表中,对每个关键字的查找概率相同,折半查找算法查找成功的平均查找长度为 37/12 ,折半查找失败的平均查找长度为 49/13
下图中圆是查找成功的结点,矩形是虚构的查找失败结点。等概率下,查找成功的 ,故ASL=(1+2*2+3*4+4*5)/12
查找失败的ASL=(3*3+4*10)/13
注:根据n的值画出折半查找判定树;查找失败的ASL不是图中的矩形结点而实,矩形结点上一层的圆形结点
4.二叉查找判定树本质为一棵二叉排序树,它的中序序列是一个有序序列。
二叉排序树性质:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的节点。
————————————————————————————————————————————————————————
————————————————————————————————————————————————————————
接着上文,之前提到了顺序查找和折半查找,顺序查找的效率低但对数据没啥要求,折半查找查找效率高但必须要求数据有序,
所以就想到能否有方法使得两个优点都具备。此外折半查找可变形为次优查找树、斐波那契查找、插值查找
一、分块查找
又称为索引顺序查找,,
核心思想:把线性表分为若干个子块,要保证块之间关键字有序,块内元素无要求。即前一块的最大关键字要小于后一块的最小关键字,依次类推。
查找步骤:(1)在索引表中确定待查记录所在的块,查找方式无要求
(2)在块内顺序查找
平均查找长度ASL=L1+Ls (索引 查找长度+块内查找长度)
将长度为n的查找表均匀分为b块,每块有s个记录,等概率下
在块内和索引表均采用顺序查找 ASL=L1+Ls=
若对索引表进行折半查找,ASL=L1+Ls=
二、二叉排序树
1.BST性质:
(1)若右子树非空,则右子树上所有关键字的值均小于根关键字的值
(2)若左子树非空,则左子树上所有关键字的值均小于根关键字的值
(3)左右子树都是二叉排序树
2.BST存储结构:通常采用二叉链表进行存储
typedef struct BTNode{
int key;
struct BTNode *lchild;
struct BTNode *rchild;
}BTNode;
3.查找关键字
在二叉树中查找某个关键字,那么这个关键字要么在根,要么在左子树,要么在右子树
由二叉树的性质得,BST中左子树的值都小于根,右子树都大于根。
那么我们可以先将关键字与根比较,
若相等,则查找成功;
如果小于根,则去左子树查找;
若大于根,则去右子树查找。
重复上述操作,若遇到空指针域,则查找失败。
BTNode * BSTSearch(BTNode * bt,int key)
{
if(bt==NULL)
return NULL;//来到了空指针域,查找失败返回NULL
else
{
if(bt->key==key)
return bt; //等于 根结点中的关键字,查找成功,返回关键字所在的结点指针
else if(key<bt->key) //小于根结点中的关键字时到左子树中查找
return BSTSearch(bt->lchild,key);
else //大于根结点中的关键字时到右子树中查找
return BSTSearch(bt->rchild,key);
}
}
4.插入
在二叉排序树中要进行插入,必须先找到插入位置,
如果关键字已经存在于树中则不可进行插入;
如果要插入的关键字小于根结点的关键字时去右子树进行插入;
如果要插入的关键字大于根结点的关键字时去左子树进行插入;
在二叉排序树找到的插入位置总是在空指针域,因此空指针域上连接的一个新结点必为叶子结点
int BSTInsert(BTNode *&bt,int key)//因为指针要改变,所以用引用型指针
{
if(bt==NULL)//当前为空指针,说明找到插入位置,创建新结点插入
bt=(BTNode*)malloc(sizeof(BTNode));//创建新结点
bt->lchild=bt->rchild=NULL;
bt->key=key; //将待插入关键字存入新结点,插入成功,返回1
return 1;//来到了空指针域,查找失败返回NULL
else //如果结点不空,则插入位置,这部分类似于查找算法
{
if(bt->key==key)
return 0; //关键字已经存在于树中,插入失败,返回0
else if(key<bt->key) //小于根结点中的关键字时到左子树中查找
return BSTInsert(bt->lchild,key);
else //大于根结点中的关键字时到右子树中查找
return BSTInsert(bt->rchild,key);
}
}
5.构造二叉排序树
建立一颗空树,然后将关键字逐个插入空树中即可构造二叉排序树
void Create(BTNode *&bt,int key[],int n){
int i;
bt=NULL;
for(i=0;i<n;++i)
BSTInsert(bt,key[i]);
}
6.删除⭐⭐⭐⭐⭐
在二叉排序树中删除一个关键字时,不能把以关键字所在的结点为根的子树都删除,而是只删除这一个结点,并保持二叉排序树的特性。
假设要删除p结点,则有3种情况:
(1)p为叶子结点。因为删除叶子结点不会破坏二叉排序树的特性,可直接删除p
(2)p结点只有右子树而无左子树(或者只有左子树而无右子树)。此时只需将p删除,然后将p的子树直接连接在原来p与其双亲结点f相连的指针上。--(只有一个子树)
人话解释:如果p原来是它双亲的左子树,删除p后,p的子树和p的双亲直接连接,此时子树是原来双亲的左子树
如果p原来是它双亲的右子树,删除p后,p的子树和p的双亲直接连接,此时子树是原来双亲的右子树
(3)p左右子树都有。⭐⭐⭐
先沿着p的左子树 根结点 指针 一直往右走,直到来到其右子树的最右边的一个结点r。
然后将p种的关键字用r种的关键字代替。
最后判断,如果r是叶子结点,则按照(1)中的方法删除r。
如果r是非叶子结点,则按照(2)中的方法删除r(此时r不可能是有两个子树的结点)
(注:从右子树根结点的左指针也可以实现,原理一样,为了减少记忆负担所以略去)
三、平衡二叉树
1.概念
平衡二叉树(别名:AVL)首先是个二叉排序树,它是特殊的二叉排序树。(比如一群比较秀的人统称为秀儿,蒂花之秀是其中比较特殊的)
(闲话:因为有人先发明了二叉排序树,实现了比较高效的查找,树越矮查找效率越高,后来有人在这个基础上改进,又发明了平衡二叉树)
定义:AVL中左右子树都为AVL,且左右子树高度之差的绝对值 不超过 1
(人们为了方便判断二叉排序树是否为二叉平衡树,引入了平衡因子)
一个结点的平衡因子为其左子树的高度 - 右子树的高度的差。故取值只能为-1,0,1
2.AVL建立
和BST基本一致,将关键字逐个插入空树,区别是——每插入一个新的关键字要检查是否插入新关键字会导致原平衡二叉树失衡。即出现平衡因子绝对值大于1的情况,失衡则要进行调整。
3.平衡调整⭐⭐⭐⭐⭐
因为新插入的会导致原来的平衡性,所以
先找出插入新结点后失去平衡的最小子树,然后调整这棵子树,使之成为平衡子树。
————————————————————————-------------------------
当失去平衡的最小子树 被 调整为平衡子树,无须调整 原有其他所有不平衡子树,整个二叉排序树就会成为一棵平衡二叉树。
最小不平衡子树:以距离插入结点最近,且 平衡因子绝对值大于1的 结点作为根的 子树
平衡调整必须保证二叉排序树左小右大的的性质。
平衡化旋转
如果一棵原本是平衡的二叉查找树中插入新结点,需要从插入位置沿通向根的路径回溯,检查各结点左右子树的高度差。
若某处不平衡则停止回溯,从发生不平衡的结点起,沿刚才回溯的路径直接取下两层的结点。
如果这三个点处于一条直线上,则采用单旋转进行平衡化;
如果这三个结点处于一条折线上,则采用双旋转进行平衡化。
平衡调整有四种:它是对不平衡状态的描述,而非调整过程的描述
LL调整(也叫右单旋调整),即新插入结点落在最小不平衡子树根结点的左孩子的左子树上
例:
某时刻在b的左子树Y上插入一个结点,导致a的左子树高度为h+2,右子树高度为h,发生不平衡。
此时应该将a下移动一个结点高度,b上移一个结点高度,
即 将b 从 a的左子树取下,然后将b的右子树挂在a的左子树上,最后将a 挂在 b的右子树上 ,达到平衡
------------------>
RR调整(也叫左单旋调整)即新插入结点落在最小不平衡子树根结点的右孩子的右子树上
LR调整(也叫先左后右双旋调整)即新插入结点落在最小不平衡子 树根结点的左孩子的右子树上
例如
某时刻b的右子树Y上插入一个结点导致不平衡。
此时须将子树Y拆分为两个子树U和V,
根结点为c,并将b的右子树、a的左子树和c的左右子树都取下。
然后将c作为a和b两颗子树的根,b为左子树,a为右子树,c原来的左子树U作为b的右子树,c原来的右子树V作为a的左子树以达到平衡
RL调整(也叫先右后左双旋调整)即新插入结点落在最小不平衡子 树根结点的右孩子的左子树上
// 这里的左和右是对应的是 不平衡状态的 一 一映射 ,而不是对应调整方式的一 一映射//
我知道right是右,left是左。
比如LL调整 它叫右单旋调整 是因为,右描述了方向
例如
如图一棵AVL树,
在ptr所指示结点60的两棵子树中较高的左子树的左侧插入一个新结点,该子树高度加1,导致根结点的平衡因子变为2
从不平衡结点60沿插入路径找到它的左子女30(改用ptr指示),这两个结点的平衡因子的(正负号)相同,都为负数,
因此需要做右单旋。
以结点30为旋转轴,让结点60顺时针旋转为30的右子女,30替代原来60的位置,原来30的右子树b转为60的左子女
平衡旋转后的形状如图