首页 > 编程语言 >折半(二分)查找算法—高效搜索算法

折半(二分)查找算法—高效搜索算法

时间:2023-10-15 23:32:20浏览次数:27  
标签:折半 二分 元素 搜索算法 算法 查找 搜索 表中

折半查找算法(Binary Search Algorithm)是一种高效的搜索算法,常用于已排序的数组或列表中进行查找操作。它的核心思想是通过比较中间元素与目标值的大小关系来确定目标值在数组的哪一部分,从而缩小搜索范围。本文将详细介绍折半查找算法的原理、实现以及应用场景。

折半(二分)查找算法—高效搜索算法_二分查找

一、原理

折半查找算法利用了已排序数组的特性,采用分治策略,将问题分解为规模更小的子问题。它的基本思路如下:

  1. 将数组的左右边界定义为low和high,初始时low为0,high为数组的长度减1。
  2. 计算中间元素的下标mid,即mid = (low + high) / 2。
  3. 比较中间元素与目标值的大小关系: a. 如果中间元素等于目标值,则返回找到的位置。 b. 如果中间元素大于目标值,则新的右边界变为mid-1。 c. 如果中间元素小于目标值,则新的左边界变为mid+1。
  4. 重复以上步骤,直到找到目标值或者左边界大于右边界。


二、实现思路

例如,在升序的查找表 {10, 14, 19, 26, 27, 31, 33, 35, 42, 44} 中查找元素 33。初始状态下,搜索区域为整个查找表,用 low 记录搜索区域内第一个元素的位置,用 high 记录搜索区域内最后一个元素的位置。


折半(二分)查找算法—高效搜索算法_数组_02


图1 搜索区域是整个查找表

二分查找算法的查找过程是:

借助 ⌊(low+high)/2⌋ 公式,找到搜索区域内的中间元素。图 1 中,搜索区域内中间元素的位置是 ⌊(1+10)/2⌋=5,因此中间元素是 27,此元素显然不是要找的目标元素。


折半(二分)查找算法—高效搜索算法_查找算法_03


图2 中间元素 27 不是目标元素

整个查找表为升序序列,根据 27<33,可以判定 33 位于 27 右侧的区域,更新搜索区域为元素 27 右侧的区域。


折半(二分)查找算法—高效搜索算法_数组_04

图3 更新搜索区域为 {31, 33, 35, 42, 44}


图 3 中,搜索区域内中间元素的位置是 ⌊(6+10)/2⌋=8,因此中间元素是 35,此元素不是要找的目标元素。


 

折半(二分)查找算法—高效搜索算法_查找算法_05

图4 中间元素 35 不是目标元素


根据 35>33,可以判定 33 位于 35 左侧的区域,更新搜索区域。


折半(二分)查找算法—高效搜索算法_折半查找_06

 图5 更新搜索区域 {31, 33}


图 5 中,搜索区域内中间元素的位置是 ⌊(6+7)/2⌋=6,因此中间元素是 31,此元素不是要找的目标元素。


折半(二分)查找算法—高效搜索算法_搜索_07

图6 中间元素 31 不是目标元素


根据 31<33,可以判定 33 位于 31 右侧的区域,更新搜索区域。



折半(二分)查找算法—高效搜索算法_折半查找_08

图7 更新搜索区域 {33}


图 7 中,搜索区域内中间元素的位置是 ⌊(7+7)/2⌋=7,因此中间元素是 33,此元素就是要找的目标元素。



折半(二分)查找算法—高效搜索算法_查找算法_09

图8 成功找到目标元素


找到了目标元素 33,二分查找算法执行结束。下面的动画演示了整个二分查找算法的执行过程:



折半(二分)查找算法—高效搜索算法_折半查找_10

图9 二分查找算法


所谓二分查找算法,其实就是不断地将有序查找表“一分为二”,逐渐缩小搜索区域,进而找到目标元素。当查找表中没有目标元素时(比如图 8 中的元素 33 为 32),最终会出现 low>high 的情况,此时就表明查找表中没有目标元素,查找失败。


三、实现

如何利用折半查找算法在已排序数组中查找特定元素的位置:

#include <stdio.h>
#include <stdlib.h>
#define keyType int

typedef struct {
    keyType key; //查找表中每个数据元素的值
    //如果需要,还可以添加其他属性
}ElemType;

typedef struct {
    ElemType* elem; //存放查找表中数据元素的数组
    int length; //记录查找表中数据的总数量
}SSTable;

//创建查找表
void Create(SSTable* st, int length) {
    int i;
    st->length = length;
    st->elem = (ElemType*)malloc((length) * sizeof(ElemType));
    printf("输入表中的元素:\n");
    //根据查找表中数据元素的总长度,在存储时,从数组下标为 1 的空间开始存储数据
    for (i = 0; i < length; i++) {
        scanf("%d", &(st->elem[i].key));
    }
}

//折半查找算法
int Search_Bin(SSTable ST, keyType key) {
    int low = 0; // 初始状态 low 指针指向第一个关键字
    int high = ST.length - 1; // high 指向最后一个关键字
    int mid;
    while (low <= high) {
        mid = (low + high) / 2; // int 本身为整形,所以 mid 每次为取整的整数
        if (ST.elem[mid].key == key) // 如果 mid 指向的同要查找的相等,返回 mid 所指向的位置
        {
            return mid;
        }
        else if (ST.elem[mid].key > key) // 如果mid指向的关键字较大,则更新 high 指针的位置
        {
            high = mid - 1;
        }
        // 反之,则更新 low 指针的位置
        else {
            low = mid + 1;
        }
    }
    //未在查找表中找到目标元素,查找失败
    return -1;
}

int main() {
    int len, key;
    int location;
    SSTable st = { 0 };
    printf("请输入查找表的长度:");
    scanf("%d", &len);
    Create(&st, len);
    printf("请输入查找数据的关键字:");
    scanf("%d", &key);
    location = Search_Bin(st, key);
    //如果返回值为 -1,证明查找表中未查到 key 值,
    if (location == -1) {
        printf("查找表中无目标元素");
    }
    else {
        printf("目标元素在查找表中的位置为:%d", location + 1);
    }
    free(st.elem);
    return 0;
}


四、应用场景

折半查找算法的时间复杂度为O(logN),其中N是数组的长度。相比于线性搜索算法的时间复杂度O(N),折半查找算法在大规模数据集上具备明显的优势。因此,它广泛应用于以下场景:


  1. 数组或列表的查找:当我们需要在一个已排序的数组或列表中查找某个特定元素时,可以使用折半查找算法进行高效的搜索。
  2. 数据库索引:数据库系统通常会采用B树(或B+树)作为索引结构,而B树的查找操作就是基于折半查找算法实现的。通过利用折半查找的特性,可以快速定位到数据库中的记录。
  3. 排序算法优化:一些排序算法,如快速排序(Quick Sort)和归并排序(Merge Sort),在内部实现中使用了折半查找算法。通过将数组划分为更小的子数组并采用折半查找的方式进行排序,可以提高算法的性能。


五、总结

二分查找算法的时间复杂度为O(logn),平均查找长度 ASL=log2(n+1)-1。和顺序查找算法相比,二分查找算法的执行效率更高。二分查找算法只适用于有序的静态查找表,且通常选择用顺序表表示查找表结构。

综上所述,折半查找算法是一种高效的搜索算法,适用于已排序的数组或列表。它通过比较中间元素与目标值的大小关系来确定目标值所在的范围,从而缩小搜索的范围,减少了搜索的时间复杂度。无论是在数据结构、数据库索引还是游戏开发等领域,折半查找算法都发挥着重要的作用。



部分内容引用:https://data.biancheng.net/

标签:折半,二分,元素,搜索算法,算法,查找,搜索,表中
From: https://blog.51cto.com/wamtar/7874059

相关文章

  • 判断二分图的方法
    题目描述:龙龙得知2020年中国将有2000万至4000万男人娶不到老婆后。他打算好好调查一下是不是人们的感情出现问题。他对n个人进行调查,得到m条信息,每条信息表示为某两人曾经是情侣。由于他不知道这些人的性别,请你帮他判断一下,有没有同性是情侣的情况?对于100%的数据,n的范围[2,100000......
  • 二分模板
    整数二分边界boolcheck(intx){/*...*/}//检查x是否满足某种性质//区间[l,r]被划分成[l,mid]和[mid+1,r]时使用:intbsearch_1(intl,intr){while(l<r){intmid=l+r>>1;if(check(mid))r=mid;//check()判断mid是......
  • 算法0506 对数器 二分搜索
    对数器非常重要的自我验证代码正确性的方法在面试时或机试时写算法题,没有测试用例或者测试用例太少,导致巨大的数据量无法进行测试时。需要自己写测试用例数据时可以使用对数器。......
  • 学习C语言心得-自定义函数-对整形有序数组进行二分查找-二分法
    对整形有序数组进行二分查找#include<stdio.h>intfind(intarr[],intsz,intk){ intleft=0;intright=sz-1; while(left<=right) { intmid=left+right/2; if(k>arr[mid]) { left=mid+1; } if(k<arr[mid]) { right=mid......
  • 二分查找(浮点二分)
    一、算法简介浮点数二分相比与整数二分就要简单很多了,但是还是要注意范围的问题。以下给出一个小例子,求\(x\)的平方根,\(x\)的范围在\([0,10000]\)内:#include<iostream>#include<cmath>usingnamespacestd;intmain(){doublen;cin>>n;dou......
  • LeetCode704. 二分查找
    描述给定一个n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果目标值存在返回下标,否则返回-1。示例1输入:nums=[-1,0,3,5,9,12],target=9输出:4解释:9出现在nums中并且下标为4示例2输入:nums=[-1,0,3,5,9,1......
  • 【二分图】第1幕:初识
    二分图的概念第1幕·第1场·二分图的概念定义若有一个无向图,其所有节点可以被分为两个不相交的非空集合,且同一集合中的点之间没有边,那么称该图为二分图。形式化地,对于一张图\(G=\{V,E\}\),若有集合\(A,B\)满足:\((A,B\subseteqV)\and(A\capB=\emptyset)=1\)\(\fora......
  • 二分答案作题心得
    使用洛谷P1873举例看出这个题目考的是二分答案找出题目横纵坐标,横坐标是我们要输出的东西(也是L和R),纵坐标是输入的m,理解题目,观察横纵坐标的递增递减关系这个题目里面输入的m是所得到的木材,横坐标是锯片的高度,锯片越高得到的木材越少,所以是递减关系开始写二分模板,写check函......
  • 代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素
    704.二分查找链接:https://leetcode.cn/problems/binary-search/description/思路:关键是定义清楚区间边界,想清楚middle在计算中是否可能取到左边界or右边界。若采用闭区间,则middle可能等于左/右边界值。27.移除元素链接:https://leetcode.cn/problems/remove-element/思路:暴......
  • 二分查找(整数二分)
    一、算法简介二分法,即二分搜索法,是通过不断缩小解可能存在的范围,从而求得问题最优解的方法。例如,如果一个序列是有序的,那么可以通过二分的方法快速找到所需要查找的元素,相比线性搜索要快不少。此外二分法还能高效的解决一些单调性判定的问题。二分的关键不在于单调性,或者说二......