【数据结构】第9章 查找! (二叉搜索树BST AVL树 B-(+)树 字典树 HASH表)

难产的笔记。。。本来打算用1天 结果前前后后拖了5天

§9.1 静态查找表

9.1.1 顺序表的查找

各种扫 自己脑补吧 复杂度O(n)

9.1.2 有序表的查找

若表是单调的,则可以利用二分查找。复杂度O(logn)

9.1.3 静态树表的查找

http://blog.csdn.net/area_52/article/details/43795837

9.1.4 索引顺序表的查找

建立索引表查找

§9.2 动态查找表

动态查找表的特点是,表结构本身是在查找过程中动态生成的,即对于给定值key,若表中存在其关键字等于key的记录,则查找成功返回,否则插入关键字等于key的记录。

9.2.1 二叉排序树和平衡二叉树

①二叉排序树及其查找过程

二叉排序树(BST)或者是一棵空树;或者是具有下列性质的二叉树:

⑴若它的左子树不空,则左子树上所有节点的值均小于它的根结点的值

⑵若它的右子树不空,则右子树上所有节点的值均大于它的根结点的值

⑶它的左右子树也分别为二叉排序树

查找代码如下

/** 递归实现二叉搜索树的查找操作Find */
Position Find( ElementType X,BinTree BST)
{
    if(!BST) return NULL;//空树
    if(X > BST->Data) return Find(X,BST->Right);
    if(X < BST->Data) return Find(X,BST->Left);
    return BST;/**找到了*/
}
/** 迭代实现二叉搜索树的查找操作Find */
Position Find( ElementType X,BinTree BST)
{
    while(BST) {
        if(X > BST->Data) {
            BST = BST->Right;
        }else if(X < BST->Data) {
            BST = BST->Left;
        }else return BST;//找到了
    }
    return NULL;//查找失败
}
/** 递归找二叉搜索树元素最小值 */
Position FindMin( BinTree BST )
{
    if(!BST) return NULL;
    else if(!BST->Left)
        return BST;//如果没有左孩子就是最小值
    else return FindMin( BST->Left );//沿左分支继续查找
}
/** 迭代找二叉搜索树元素最大值 */
Position FindMax( BinTree BST )
{
    if( BST ) {
        while(BST->Right) BST = BST->Right;
    }
    return BST;
}

②二叉排序树的插入和删除

⑴BST中结点的插入

新插入的结点一定是一个新添加的叶子结点,并且是查找不成功时查找路径上访问的最后一个结点的左孩子或者右孩子。因此把查找算法改一下就可以了。

一个无序序列可以通过构造一棵二叉排序树而变成一个有序序列,构造树的过程即为对无序序列进行排序的过程。

⑶BST中结点的删除

分三种情况处理:

1.删除的是叶子结点,那么直接删除即可。

2.删除的结点只有左子树或右子树,那么直接删除然后把子树放到它父亲上即可。

3.删除的结点左子树和右子树均不空。有两种做法,一是左子树接上来,右子树放到左子树的最右边。二是用直接前驱(后继)代替它,然后删除它的直接前驱(后继)(具体见P230)

删除的代码如下

Status DeleteBST(BiTree &T, KeyType key)
{
    //若二叉排序树T中存在关键字等于key的数据元素时,
    //则删除该数据元素结点,并返回TRUE,否则返回FALSE
    if(!T) return FALSE;//不存在关键字等于key的数据元素
    if(EQ(key,T->data.key)) return Delete(T);//找到关键字等于key的数据元素
    else if(LT(key,T->data.key)) return DeleteBST(T->lchild,key);
    else return DeleteBST(T->rchild,key);
}
Status Delete(BiTree &p)
{
    //从二叉排序树中删除结点p,并重接它的左子树或右子树
    if(!p->rchild) {//右子树空则只需要重接它的左子树
        q = p;
        p = p->lchild;
        free(q);
    }else if(!p->lchild) {//只需重接它的右子树
        q = p;
        p = p->rchild;
        free(q);
    }else {//左右子树都不空
        q = p;
        s = p->lchild;
        while(s->rchild) {//转左,然后向右到尽头
            q = s;
            s = s->rchild;
        }
        p ->data = s ->data;//s指向被删除结点的前驱
        if(q != p) q ->rchild = s->lchild;//重接*q的右子树
        else q -> lchild = s -> lchild; //重接*q的左子树
        delete s;
    }
    return TRUE;
}

③二叉排序树的查找分析

对ASL的计算发现,如果二叉树趋于”平衡”,那么它的ASL就可以有效降低,效率提高。在某些情况下,尚需在构成二叉排序树的过程中进行”平衡化”处理,称为二叉平衡树。

④平衡二叉树(AVL树)

平衡二叉树又称AVL树。它或者是一棵空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。

若将二叉树上结点的平衡因子BF定义为该结点的左子树的深度减去它的右子树的深度,则平衡二叉树上所有结点的平衡因子只可能是-1,0,1。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。

n层的AVL树的最小结点数符合类斐波那契

1层的AVL树最小需要1个结点,2层的AVL树最小需要2个结点,3层的至少需要4个结点,4层的最小需要7个结点…

以此类推n层的最少结点=(n-1)层最少结点+(n-2)层最少结点+1

在AVL树中,插入一个新结点很有可能意味着失去平衡。在这种情况下要找出失去平衡的最小树根结点的指针,然后再调整这个子树中有关结点之间的链接关系,使之成为新的平衡子树。当失去平衡的最小子树被调整为平衡子树后,原有其他所有不平衡子树无序调整,整个二叉排序树就又成为一颗平衡二叉树。

失去平衡的最小子树是指以离插入节点最近且平衡因子异常(绝对值>1)的结点作为根的子树。

假设用A来表示失去平衡最小子树的根结点,则调整该子树的操作无外乎有下面4种情况

⑴RR旋转

新的结点插在了A的右子树的右子树上(不管插在左还是插在右),那么将A的右子树提上去当树根,原A的右子树的左子树插在A的右边。如图所示

⑵LL旋转

新的结点插在了A的左子树的左子树上(不管插在左还是插在右),那么将A的左子树提上去当树根,原A的左子树的右子树拿到A的左子树上。如图所示

⑶RL旋转

新的结点插在了A的右子树的左子树上(不管插在左还是插在右),此时的重点是要把A C D三个结点调平衡。D是三个结点中中间大的,所以D去做树根,A做左子树,C做右子树。剩下的结点按照大小顺序找到位置即可。

⑷LR旋转

新的结点插在了A的左子树的右子树上(不管插在左还是插在右),此时的重点是要把A B D三个结点调平衡。D是三个结点中中间大的,所以D去做树根,B做左子树,A做右子树。剩下的结点按照大小顺序找到位置即可。

⑤平衡树查找的分析

查找的时间复杂度为O(logn)

9.2.2 B-树和B+树

B树待补充….

①B-树及其查找

②B-树查找分析

③B-树的插入和删除

④B+树

9.2.3 键树

键树又称数字查找树(DST)。它是一棵度>=2的树,树中的每个节点中不是包含一个或几个关键字,而是只含有组成关键字的符号。

例如,若关键字是数值,则结点中只包含一个数位;若关键字是单词,则结点中只包含一个字母字符。

这种书会给某种类型关键字的查找带来方便。

如图就是一棵键树,从根到叶子结点路径中结点的字符组成的字符串表示一个关键字。

我们约定,键树是有序树

键树有两种存储结构,从而产生了两种键树

①孩子兄弟表示法的双链树

每个分支结点包括3个域

symbol域 存储关键字的一个字符。

first域 存储指向第一棵子树根的指针。

next域 存储指向右兄弟的指针。

同时,叶子结点的first域存储指向该关键字记录的指针。

双链树的实现代码

#define MAXKEYLEN 16 //关键字的最大长度
typedef struct {
    char ch[MAXKEYLEN];//关键字
    int num;//关键字长度
}KeysType;//关键字类型
typedef enum {LEAF,BRANCH} NodeKind;//结点种类:{叶子,分支}
typedef struct DLTNode {
    char symbol;
    struct DLTNode *next;//指向兄弟结点的指针
    NodeKind kind;
    union {
        Record *infoptr;//叶子结点的记录指针
        struct DLTNode *first;//分支结点的孩子链指针
    };
}DLTNode, *DLTree;//双链树的类型

双链树的查找代码

Record *SearchDLTree(DLTree T,KeysType K)
{
    //在非空双链树T中查找关键字等于K的记录,若存在,则
    //返回指向该记录的指针,否则返回空指针
    p = T->first;
    i = 0;//初始化
    while(p && i < K.num) {
        while(p && p -> symbol != K.ch[i]) p = p->next;//查找关键字的第i位
        if(p && i < K.num-1) p = p->first;//准备查找下一位
        ++ i;
    }//查找结束
    if(!p) return NULL;//查找不成功
    else return p->infoptr;//查找成功
}//Search DLTree

键树中每个节点的最大度d和关键字的”基”有关,若关键字是单词,则d=27;若关键字是数值,则d=11。键树的深度h则取决于关键字中字符或数位的个数。

②多重链表表示的Trie树(字典树)

若以树的多重链表表示键树,则树的每个节点中应含有d个指针域,此时的键树又称Trie(读音同Try)树或前缀树。Trie树可以看做是确定有限状态的自动机

如下图就是一个Trie树

Trie树的性质:

①根节点不包含字符,除根节点外每一个节点都只包含一个字符。

②从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。

③每个节点的所有子节点包含的字符都不相同。

在Trie树上进行查找的过程为:

从根结点出发,沿和给定值相应的指针逐层向下,直至叶子结点,若叶子结点中的关键字和给定值相等,则查找成功,若分支结点中和给定值相应的指针为空,则查找不成功。

查找算法代码如下

typedef struct TrieNode {
    NodeKind kind;
    union {
        struct {KeysType K; Record *infoptr;}  lf;//叶子结点
        struct {TrieNode *ptr[27]; int num;}   bh;//分支结点
    };
}TrieNode, * TrieTree;
Record *SearchTrie(TrieTree T,KeysType K)
{
    //在字典树T中查找关键字等于K的记录
    for(p = T,i = 0;//对K的每个字符逐个查找
        p && p -> kind == BRANCH && i < K.num;//*p为分支结点
        p = p->bh.ptr[ord(K.ch[i])], ++i;);//ord求字符在字母表中序号
    if(p && p->kind == LEAF && p->lf.K == K) return p -> lf.infoptr;//查找成功
    else return NULL;//查找不成功
}//SearchTrie

§9.3 哈希表

9.3.1 什么是哈希表

根据设定的哈希函数H(key)和处理冲突的方法将一组关键字映像到一个有限的连续的地址集(区间)上,并以关键字在地址集中的”像”作为记录在表中的存储位置,这种表变称为哈希表。这一映像过程称为哈希造表散列,所得存储位置称哈希地址散列地址

9.3.2 哈希函数的构造方法

①直接定址法

取key或者key的某个线性函数值为哈希地址。即

H(key) = key 或 H(key) = A*key+B

直接定址法使用很少

②数字分析法

取key的若干数位组成哈希地址。(如身份证可以使用最后四位组成哈希地址)

③平方取中法

取key的平方的中间几位为哈希地址(具有随机性)

④折叠法

将key分割成位数相同的几部分(最后一部分的位数可以不同),然后取这几部分的叠加和作为哈希地址。

如H(123456) = 12+34+56 = 102 这样可以使每一位都对结果有影响,增加随机性

※⑤除留余数法

取关键字被某个不大于哈希表表长m的数p(通常取素数)除后所得余数为哈希地址。即

* H(key) = key%p(p<=m) *

⑥随机数法

通常用于key长度不等

9.3.3 处理冲突的方法

①开放定址法

LOC = (H(key) + di)%m (i=1,2,…,m-1)

可以取

线性探测再散列(最常用)

(di = 1,2,….,m-1)

⑵平方探测再散列

(di = 1^2,-1^2,2^2,-2^2,…,k^2,-k^2)(k<=m/2)

②再哈希法

在哈希函数寻址的基础上再建立一个哈希函数

Hi = RHi(key) i = 1,2,…,k

RHi均是不同的哈希函数,即在同义词产生地址冲突时计算另一个哈希函数地址,直到冲突不再发生。这种方法不易产生”聚集”,但增加了计算的时间。

链地址法(拉链法) (第二常用)

为了可以重复放入,所有位置均为链表形式,初始状态是空指针,凡是哈希函数是i的记录都插入第i个链表的末尾。

△④建立一个公共溢出区

9.3.4 哈希表的查找及其分析

在哈希表上进行查找的过程和哈希造表的过程基本一致。

哈希表的装填因子α定义为

α = 表中填入的记录数/哈希表的长度

哈希表的平均查找长度时α的函数,而不是n的函数。由此,不管n多大,我们总可以选择一个合适的装填因子以便将平均查找长度限定在一个范围内。

【至此第9章整理完毕】

时间: 2024-10-16 21:07:22

【数据结构】第9章 查找! (二叉搜索树BST AVL树 B-(+)树 字典树 HASH表)的相关文章

数据结构(六)查找---二叉搜索树(排序树)

前提 前面的查找我们都是静态查找,因为数据集是有序存放,查找的方法有多种,可以使用折半,插值,斐波那契等,但是因为有序,在插入和删除操作上的效率并不高. 这时我们就需要一种动态查找方法,既可以高效实现查找,又可以使得插入和删除效率不错,这时我们可以考虑二叉排序树 二叉排序树 一:定义 又称为二叉搜索树(查找树),是一棵树,可以为空,但是需要满足以下性质: 1.非空左子树的所有键值小于其根节点的键值 2.非空右子树的所有键值大于其根节点的键值 3.左右子树都是二叉搜索树 二:操作 查找 /* Bi

数据结构中常见的树(BST二叉搜索树、AVL平衡二叉树、RBT红黑树、B-树、B+树、B*树)

树 即二叉搜索树: 1.所有非叶子结点至多拥有两个儿子(Left和Right): 2.所有结点存储一个关键字: 非叶子结点的左指针指向小于其关键字的子树,右指针指向大于其关键字的子树: 如: BST树的搜索,从根结点开始,如果查询的关键字与结点的关键字相等,那么就命中: 如果BST树的所有非叶子结点的左右子树的结点数目均保持差不多(平衡),那么B树 的搜索性能逼近二分查找:但它比连续内存空间的二分查找的优点是,改变BST树结构 插入与删除结点)不需要移动大段的内存数据,甚至通常是常数开销: 如:

数据结构-二叉搜索树(BST binary search tree)

本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/ 二叉搜索树简介 顾名思义,二叉搜索树是以一棵二叉树来组织的,这样的一棵树可以用一个链表数据结构来表示,每个节点除了key和卫星数据(除了二叉树节点的基本数据以外人为添加的数据,这些数据和树的基本结构无关),还有left.right.parent,分别指向节点的左孩子.右孩子和父节点,如果对应的节点不存在则指向NIL节点(因为最简单的二叉搜索树中的NIL节点里并没有有用的信息,所以在实现的时候简

数据结构之二叉搜索树、AVL自平衡树

前言 最近在帮公司校招~~ 所以来整理一些数据结构方面的知识,这些知识呢,光看一遍理解还是很浅的,看过跟动手做过一遍的同学还是很容易分辨的哟~ 一直觉得数据结构跟算法,就好比金庸小说里的<九阳神功>,学会九阳神功后,有了内功基础,再去学习其他武功,速度就有质的提升 内容大概包含这些,会分多篇文章来整理: 二叉搜索树 平衡二叉树(AVL) 二叉堆 堆排序 四叉树 八叉树 图,深度优先DFS.广度优先BFS 最短路径 二叉树 二叉树,也就是每个节点最多有两个孩子的树.多用于搜索,查找,还有可以用来

[数据结构]二叉搜索树(BST) VS 平衡二叉排序树(AVL) VS B树(平衡多路搜索树) VS B+树 VS 红黑树(平衡二叉B树)

1 二叉排序树/二叉查找树/Binary Sort Tree 1种对排序和查找都很有用的特殊二叉树 叉排序树的弊端的解决方案:平衡二叉树 二叉排序树必须满足的3条性质(或是具有如下特征的二叉树) 若它的左子树不为空,则:左子树上所有结点的值< 它根结点的值 若它的右子树不为空,则:右子树上所有结点的值 > 它根结点的值 它的左子树.右子树也分别为二叉排序树(递归性) (按照如上定义,即: 1 无键值相等的结点 2 中序遍历一颗二叉树时,可得一个结点值递增的有序序列) 2 平衡二叉排序树/Bal

二叉树、二叉搜索树、AVL树的java实现

数据结构一直都是断断续续的看,总是觉得理解的不够深入,特别是对树的理解,一直都很浅显,今儿又看了一遍,来做个总结吧. 首先,树中的一些概念: 1.树的节点包含一个数据元素,以及若干指向其子树的分支.节点拥有的子树的数量称为节点的度.节点的最大层次称为树的深度或高度. 2.二叉树是一种树形结构,其特点是每个节点至多有两棵子树,且子树有左右之分,次序不能随意颠倒. 3.满二叉树:一棵深度为k且有2^k - 1个节点的二叉树,称之为满二叉树. 4.完全二叉树:对一个深度为k,节点个数为n的二叉树,当且

平衡二叉搜索树(AVL树)的原理及实现源代码(有图文详解和C++、Java实现代码)

一.AVL树(平衡二叉搜索树)是什么? AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的.AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个非叶子结点的左右子树的高度之差的绝对值(平衡因子)最多为1. 例如: 5             5 / \            /  \ 2   6         2   6 / \    \         / \ 1  4   7       1  4

算法二叉搜索树之AVL树

最近学习了二叉搜索树中的AVL树,特在此写一篇博客小结. 1.引言 对于二叉搜索树而言,其插入查找删除等性能直接和树的高度有关,因此我们发明了平衡二叉搜索树.在计算机科学中,AVL树是最先发明的自平衡二叉搜索树.在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树.对于N个节点的AVL树,由于树高被限制为lgN,因此其插入查找删除操作耗时为O(lgN). 2.旋转 在讲解关键步骤插入与删除以前,首先我们先定义一些辅助用的操作:旋转.旋转分为左旋和右旋,其示意图如下: 相信上

算法导论—二叉搜索树(BST)

华电北风吹 天津大学认知计算与应用重点实验室 日期:2015/9/9 与散列表一样,搜索树数据结构也支持动态集合操作,包含插入,查询,删除,最小值,最大值,前驱,后继等. 一.二叉搜索树: 二叉搜索树节点:关键字key,卫星数据,左孩子指针,右孩子指针,父节点指针,其他特殊类型(红黑树的节点颜色,AVL树的树高等). 二叉搜索树性质:x是二叉搜索树中的任意一个节点.若y是x左子树中任意一个节点有x.key>=y.key.若y是x右子树中任意一个节点有x.key<=y.key. 二.二叉搜索树的