数据结构之查找(图片来源,老师PPT)

顺序查找进行遍历元素,进行查找

总计全部比较次数为:1+2+…+n = (1+n)n/2

若求某一个元素的平均查找次数,还应当除以n(等概率), 即: ASL=(1+n)/2 ,时间效率为 O(n)

解释:ASL表示average search length 平均查找长度

public static void main(String[] args) {
    int[] a=new int[] {5,1,6,9,2,4,8};
    System.out.println(linearSearch(a, 9));
}

public static int linearSearch(int[] arr,int searchNum) {
    int res=-1;
    for(int i=0;i<arr.length;i++) {
        if(arr[i]==searchNum) {
            res=i;
            break;
        }
    }
    return res;
}

2.二分查找法

前提,序列有序

平均每个数据的查找时间还要除以n,所以:

public static void main(String[] args) {
    int[] a=new int[] {1,2,3,4,5,6,7};
    System.out.println(linearSearch(a, 3));
}

public static int binarySearch(int[] arr,int searchNum) {
    int low,high,middle;
    low=0;
    high=arr.length-1;
    middle=(low+high)/2;
    for(int i=0;i<arr.length;i++) {
        if(searchNum==arr[middle]) {
            return middle;
        }else if(searchNum>arr[middle]) {
            low=middle;
            middle=(low+high)/2;
        }else {
            high=middle;
            middle=(low+high)/2;
        }
    }
    return -1;
}

查找过程可用二叉树描述:每个记录用一个结点表示;结点中值为该记录在表中位置,这个描述查找过程的二叉树称为判定树。

n个元素的表的二分查找的判定树是唯一的,即:判定树由表中元素个数决定。

3.分块查找

这是一种顺序查找的另一种改进方法。 先让数据分块有序,即分成若干子表,要求每个子表中的数值(用关键字更准确)都比后一块中数值小(但子表内部未必有序)。

然后将各子表中的最大关键字构成一个索引表,表中还要包含每个子表的起始地址。

 

① 对索引表使用二分查找法(因为索引表是有序表);

② 确定了待查关键字所在的子表后,在子表内采用顺序查找法(因为各子表内部是无序表);

查找效率分析:ASL=Lb+Lw

 

前提:自定义每个区块的数量会影响查找的速度,分块查找在大数据中也有一定的作用,类似大数据的分机器先排序内,再排序外的排序法,只不过这边的区块内部是有序的

4.散列查找(哈希查找)

哈希(Hash)表:即散列存储结构。 散列法存储的基本思想:建立关键码值与其存储位置的对应关系,或者说,由关键码的值决定数据的存储地址。

优点:查找速度极快(O(1)),查找效率与元素个数n无关!

按照计算哈希直接存入计算机地址,Java可以用一个数组空间进行模拟,只要进行计算即可

 

  • 哈希函数的构造方法

    直接定址法

    Hash(key) = a·key + b (a、b为常数) 优点:以关键码key的某个线性函数值为哈希地址,不会产生冲突. 缺点:要占用连续地址空间,空间效率低。

    除留余数法

    Hash(key)=key mod p (p是一个整数) 特点:以关键码除以p的余数作为哈希地址。

    关键:如何选取合适的p? 技巧:若设计的哈希表长为m,P通常取小于或等于散列表长m的最大素数(p≤m),Java中选用了31,因为31正好是2<<5-1

    平方取中法

    特点:对关键码平方后,按哈希表大小,取中间的若干位作为哈希地址。

     理由:因为中间几位与数据的每一位都相关。 例:2589的平方值为6702921,可以取中间的029为地址。

 

    折叠法

    特点:将关键码自左到右分成位数相等的几部分(最后一部分位数可以短些),然后将这几部分叠加求和(舍去进位)作为散列地址。

     法1:移位法 ── 将各部分的最后一位对齐相加。

    法2:间界叠加法──从一端向另一端沿分割界来回折叠后,最后一位对齐相加。

    例:元素42751896, 用法1: 427+518+96=1041 用法2: 427 518 96—> 724+518+69 =1311

 

5.二叉排序树(又称二叉查找树,中序序列顺序的树)

   关于查找:与根节点比较,如果大则右查找,如果小为左查找

  关于插入:按照查找进行插入

  关于删除,如果是叶子节点直接删除,如果有一个子树,那么删除后把子节点回归,如果有左右两个子树,那么把左子树的最大或者右子树的最小放在原来节点

6.平衡二叉树(AVL树,又称红黑树)(可参考这个博客:https://blog.csdn.net/saasanken/article/details/80796178,关于AVL树)

  平衡二叉树又称AVL树,它是具有如下性质的 二叉树:

  左、右子树是平衡二叉树; 所有结点的左、右子树深度之差的绝对值≤ 1

  即|左子树深度-右子树深度|≤ 1

  为了方便起见,给每个结点附加一个数字,给 出该结点左子树与右子树的高度差。这个数字 称为结点的平衡因子balance。这样,可以得到 AVL树的其它性质:

  如果在一棵AVL树中插入一个新结点,就有可能造成失衡,此时必须重新调整树的结构,使之恢复平衡。我们称调整平衡过程为平衡旋转。

  平衡旋转可以归纳为四类:

优点和缺点:

优点:层数较少,可以尽快查到

缺点:插入时需要旋转多次,这样会小号大量的磁盘IO

7.B-树,B+树,B*树

2-3树(B树的引入)

1)2节点包含一个元素和2个孩子(或者没有孩子)

左子树小于元素,右子树大于元素

2)3节点,包含2个元素,一大一小,三个孩子(或者没有孩子)

左子树节点元素值小于节点元素较小元素值,右节点包含元素大于节点元素较大元素值

3)2-3树所有叶子接待你都在同一层

 

                              图2-3树(图片来源:中国大学mooc)

 

2-3-4树(B树的引入)

1)23节点与23树相似

2)4节点包含3个元素,4个孩子

3)叶子节点也在同一个层级

 

B-树

B tree,又称B树,B为Balanced

把树中最大的孩子称为B树的阶,一颗M阶的空树,或为满足如下特性的M叉树:

1)树中每个节点至多有M棵子树(有M-1)个关键字

2)若根节点不是终端节点,则至少有2棵子树。(1个关键字)

3)除了根节点以外,非叶子节点至少有【M/2】棵子树。(含有M/2-1个关键字),半满来使空间减少浪费

4)所有非叶子接待你结构都在同一层次上,不带信息。(因为折半查找树中最终的叶子接待你会导致查找失败

关于查找:与二叉排序树类似

因为B树中有多个元素,而且也不用像红黑树一样进行旋转,所有,B树的查找性能优于二叉排序树

弊端:除非重建树,否则无法改变值的最大长度

B+树

常用于数据库和操作系统的文件系统中的一种用于查找的数据结构

B+树的查找性能优于B树

差异:

1)B+树中,具有N个关键字的节点只含有N棵子树,每个关键字对应一棵子树;B树中,N个关键字含有N+1棵子树

2)B+树中,每个节点的关键字个数的范围是[m/2]<=n<=m根节点1<=n<=m,而在B树中,范围比B+树多1

3)每个父亲节点都指向孩子节点的最大关键字

4)B+树中,叶子接待你包含了所有关键字,非叶子接待你中出现的关键字也出现在叶子节点中,叶子节点的指针指向记录,而在B树中,叶子节点的关键字与非叶子节点的关键字加起来才是不重复的

5)B+树中,所有叶子节点构成一个单链表

                                  图B+树(图片来源:中国大学mooc)

 

B-树和B+树区别

B+树中间层节点存储的是下一层的地址,并没有存储数据。B-树中间层是会存储数据的,所以遍历数据时磁盘IO次数会增多,所有我们常常在数据库和操作系统中选择B+树进行存储数据

 

B*树

B*树是B+树的变体,在B+树的非根和非叶子结点再增加指向兄弟的指针;B*树定义了非叶子结点关键字个数至少为(2/3)*M,即块的最低使用率为2/3(代替B+树的1/2)。

B+树的分裂:当一个结点满时,分配一个新的结点,并将原结点中1/2的数据复制到新结点,最后在父结点中增加新结点的指针;B+树的分裂只影响原结点和父结点,而不会影响兄弟结点,所以它不需要指向兄弟的指针;
B*树的分裂:当一个结点满时,如果它的下一个兄弟结点未满,那么将一部分数据移到兄弟结点中,再在原结点插入关键字,最后修改父结点中兄弟结点的关键字(因为兄弟结点的关键字范围改变了);如果兄弟也满了,则在原结点与兄弟结点之间增加新结点,并各复制1/3的数据到新结点,最后在父结点增加新结点的指针;

 

posted @ 2019-06-13 14:58  SteveYu  阅读(695)  评论(0编辑  收藏  举报