数据结构学习总结(七)查找

概论

查找表:由同一类型的数据元素(或记录)构成的集合
关键字:数据元素中某个数据元素的值
主关键字:可以唯一地标识一个记录的关键字
次关键字:可以识别多个数据元素(或记录)的关键字

查找就是根据给定的某个值,在查找表中确定一个其关键字等于给定的数据元素(或记录)

查找表按操作方式分为两大种:静态查找表和动态查找表
静态查找表:只作查找操作的表
动态查找表:在查找过程中同时插入查找表中不存在的数据元素,或者删除已经存在的某个数据元素

从逻辑上说,查找所基于的数据结构是集合,集合中的数据元素(记录)之间没有本质关系。可是想要获得较高的查找性能,就得改变数据元素之间的关系,在存储时将查找集合组织成表、树等结构。

例如,对于静态查找表来说,用线性表结构来组织数据,这样可以使用顺序查找算法,如果再对主关键字排序,则可以应用折半查找等技术进行高效查找。对于动态查找,可以考虑二叉排序树的查找技术。

顺序查找

顺序查找又叫线性查找,查找过程是从表中第一个(或最后一个)记录开始依次查找。

顺序表查找算法:

/*顺序查找,a为数组,key 为要查找的关键字*/
public int sequential_search(int[] a, int key){
    for (int i = 1; i < a.length; i++) {
        if (a[i] == key){
            return i;
        }
    }
    return 0;
}

顺序表查找算法优化:在查找方向尽头放置 “哨兵” 免去了在查找过程中每一次比较后都要判断查找位置是否越界

/*哨兵顺序查找*/
public int sequential_search2(int[] a, int key){
    a[0] = key;/*设置a[0]为关键字值,我们称之为哨兵*/
    int i = a.length-1;
    while (a[i] != key){
        i--;
    }
    return i;/*返回0则说明查找失败*/
}

有序表查找

下面介绍三种有序表查找算法,这三种查找的前提待查找的查找表必须顺序存储并且有序
这三种有序表的查找本质上在于分割点的选取不同

折半查找

折半查找又称二分查找,其基本思想是:在有序表中,取中间记录作为比较对象,若给定值与中间记录的关键字相等,则查找成功;若给定值小于中间记录的关键字,则在中间记录的左半区继续查找;若大于,则在右半区继续查找。

折半查找算法:

/*折半查找*/
public int binary_search(int[] a, int key) {
    int low = 1, high = a.length-1;
    while (low <= high) {
        int mid = (low + high) / 2;/*折半*/
        if (key < a[mid]) {
            high = mid-1;
        } else if (key > a[mid]) {
            low = mid+1;
        } else {
            return mid;
        }
    }
    return 0;
}

插值查找

插值查找是根据要查找的关键字 key 与查找表中最大最小记录的关键字比较后的查找算法,其核心就在于插值计算公式 k e y − a [ l o w ] a [ h i g h ] − a [ l o w ] \frac{key-a[low]}{a[high]-a[low]} a[high]a[low]keya[low]

对于表长比较大,而关键字分布比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好得多。

将上述代码 mid 计算公式作如下替换:

mid = low + (high - low)*(key - a[low])/(a[high] - a[low]);/*插值*/

斐波那契查找

斐波那契数列又称黄金分割数列,该数列越往后相邻的两个数的比值越趋向于黄金比例值(0.618)。斐波那契查找就是在二分查找的基础上根据斐波那契数列进行分割的。

斐波那契查找的核心是:
1)当 key=a[mid] 时,查找成功;
2)当 key<a[mid] 时,新的查找范围是第 low 个到第 mid-1 个,此时范围个数为 F[k-1] - 1 个,即数组左边的长度;
3)当 key>a[mid] 时,新的查找范围是第 mid+1 个到第 high 个,此时范围个数为 F[k-2] - 1 个,即数组右边的长度。
在这里插入图片描述

/*斐波那契查找*/
public static int fibonacci_search(int[] data, int key) {
    int low = 1, high = data.length-1, k = 0, n = data.length-1;
    while (n > fib(k) - 1) {/*计算 n 位于斐波那契数列的位置*/
        k++;
    }
    int[] a = Arrays.copyOf(data, fib(k) - 1);
    for (int i = n; i < fib(k) - 1; i++) {/*将数组a扩展到F[k]-1的长度,并补全数组*/
        a[i] = a[n];
    }
    while (low <= high) {
        int mid = low + fib(k - 1) - 1;/*计算当前分割的下标*/
        if (key < a[mid]) {
            high = mid - 1;
            k--;
        } else if (key > a[mid]) {
            low = mid + 1;
            k -= 2;
        } else {
            if (mid <= n) {
                return mid;
            } else {
                return n;
            }
        }
    }
    return 0;
}

/*斐波那契递归函数*/
private static int fib(int k) {
    if (k == 1 || k == 2) {
        return 1;
    }
    return k > 2 ? fib(k - 1) + fib(k - 2) : 0;
}

二叉排序树

二叉排序树,又叫二叉查找树,它或者是一棵空树;或者是具有以下性质的二叉树:

  1. 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  2. 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  3. 它的左右子树也分别为二叉排序树

构造一颗二叉排序树的目的,其实并不是为了排序,而是为了提高查找和插入关键字的速度。如下图就是一棵二叉排序树。
二叉排序树

查找操作

查找算法的基本思想是:若查找树不为空,将待查关键字与根结点元素关键字比较,若相等则返回根结点;否则判断待查关键字与根结点关键字的大小,如果待查关键字小,则递归的在查找树的左子树中查找,否则递归的在查找树的右子树中查找。

在这里插入图片描述
例如在图(a)所示的二叉查找树中查找元素 13,就从根结点 15 开始向左、右、右走了一条到达 13 的路径,在图中用虚线表示。

插入操作

为了判定新结点的插入位置,需要从根结点开始逐层深入,判断新结点关键字与各子树根结点关键字的大小,若新结点关键字小,则向相应根结点的左子树深入,否则向右子树深入;直到向某个结点的左子树深入而其左子树为空,或向某个结点的右子树深入而其右子树为空时,则确定了新结点的插入位置。下图为插入结点 14 的过程。
在这里插入图片描述

删除操作

同样在二叉查找树中删除一个特定结点 v 时,我们也要在保持二叉查找树特性的前提下进行删除。与在二叉查找树中插入结点不同的是,在二叉查找树中删除结点要复杂的多,因为在二叉查找树中插入的新结点总是作为叶子结点插入,只要找到插入位置则插入操作十分简单,而在二叉查找树中删除的结点不总是叶子结点,因此在删除一个非叶子结点时需要处理该结点的子树。

下面我们分三种情况讨论结点 v 的删除:
(1)如果结点 v 为叶子结点,此时可以直接删除该叶子结点v,而不会破坏二叉查找树的特性,因此直接从树中摘除v即可。例如在图 8-4 所示的二叉查找树中删除结点 2,由于结点 2 是叶子结点,则直接摘除即可。
(2)如果结点v只有左子树Pl或只有右子树Pr,此时,当结点v是左孩子时,只要令Pl或Pr为
其双亲结点的左子树即可;当结点v是右孩子时,只要令Pl或Pr为其双亲结点的右子树即可。
例如在图 8-4 所示的二叉查找树中删除结点 3 和 7,由于 3 是左孩子,因此在删除结点 3 之
后,将其右子树设为其父结点 6 的左子树即可;同样,因为 7 是右孩子,因此在删除结点 7
之后,将其右子树设为其父结点 6 的右子树即可。
(3)如果结点v既有左子树Pl又有右子树Pr,此时,不可能进行如上简单处理。为了在删
除结点v之后,仍然保持二叉查找树的特性,我们必须保证删除v之后,树的中序序列必须仍
然有序。为此,我们可以先用中序序列中结点v的前驱或后序替换v,然后删除其前驱或后序

平衡二叉树(AVL 树)

平衡二叉树(Height-Balanced Binary Search Tree),是一种二叉排序树,其中每一个节点的左子树和右子树的高度差至多等于1。

将二叉树上节点的左子树深度减去右子树深度的值称为平衡因子(Balance Factor),那么平衡二叉树上所有节点的平衡因子只可能是 -1、0、1。

距离插入节点最近的,且平衡因子的绝对值大于 1 的节点为根的子树,称为最小不平衡子树。

平衡二叉树实现原理

多路查找树(B 树)

多路查找树(muitl-way search tree),其中每一个节点的孩子数可以多于两个,且每一个节点处可以存储多个元素。

2-3 树

2-3树

2-3-4 树

B 树

B 树是一种平衡的多路查找树,2-3 树和 2-3-4 树都是 B 树的特例。节点最大的孩子数目称为 B 树的阶(order),因此,2-3 树是 3 阶 B 树,2-3-4 树是 4 阶 B 树。

B+ 树

散列表查找(哈希表)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
逻辑结构:描述数据元素之间的逻辑关系,如线性结构(如数组、链表)、树形结构(如二叉树、堆、B树)、图结构(有向图、无向图等)以及集合和队列等抽象数据类型。 存储结构(物理结构):描述数据在计算机中如何具体存储。例如,数组的连续存储,链表的动态分配节点,树和图的邻接矩阵或邻接表表示等。 基本操作:针对每种数据结构,定义了一系列基本的操作,包括但不限于插入、删除、查找、更新、遍历等,并分析这些操作的时间复杂度和空间复杂度。 算法: 算法设计:研究如何将解决问题的步骤形式化为一系列指令,使得计算机可以执行以求解问题。 算法特性:包括输入、输出、有穷性、确定性和可行性。即一个有效的算法必须能在有限步骤内结束,并且对于给定的输入产生唯一的确定输出。 算法分类:排序算法(如冒泡排序、快速排序、归并排序),查找算法(如顺序查找、二分查找、哈希查找),图论算法(如Dijkstra最短路径算法、Floyd-Warshall算法、Prim最小生成树算法),动态规划,贪心算法,回溯法,分支限界法等。 算法分析:通过数学方法分析算法的时间复杂度(运行时间随数据规模增长的速度)和空间复杂度(所需内存大小)来评估其效率。 学习算法与数据结构不仅有助于理解程序的内部工作原理,更能帮助开发人员编写出高效、稳定和易于维护的软件系统。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值