数据结构和算法

DSA 主页 DSA 概述 DSA 环境设置 DSA 算法基础 DSA 渐近分析

数据结构

DSA 数据结构基础 DSA 数据结构和类型 DSA 数组数据结构

链接列表

DSA 链接列表数据结构 DSA 双向链接列表数据结构 DSA 循环链表数据结构

堆栈 &队列

DSA 堆栈数据结构 DSA 表达式解析 DSA 队列数据结构

搜索算法

DSA 搜索算法 DSA 线性搜索算法 DSA 二分搜索算法 DSA 插值搜索 DSA 跳跃搜索算法 DSA 指数搜索 DSA 斐波那契搜索 DSA 子列表搜索 DSA 哈希表

排序算法

DSA 排序算法 DSA 冒泡排序算法 DSA 插入排序算法 DSA 选择排序算法 DSA 归并排序算法 DSA 希尔排序算法 DSA 堆排序 DSA 桶排序算法 DSA 计数排序算法 DSA 基数排序算法 DSA 快速排序算法

图形数据结构

DSA 图形数据结构 DSA 深度优先遍历 DSA 广度优先遍历 DSA 生成树

树数据结构

DSA 树数据结构 DSA 树遍历 DSA 二叉搜索树 DSA AVL 树 DSA 红黑树 DSA B树 DSA B+ 树 DSA 伸展树 DSA 尝试 DSA 堆数据结构

递归

DSA 递归算法 DSA 使用递归的汉诺塔 DSA 使用递归的斐波那契数列

分而治之

DSA 分而治之 DSA 最大最小问题 DSA 施特拉森矩阵乘法 DSA Karatsuba 算法

贪婪算法

DSA 贪婪算法 DSA 旅行商问题(贪婪方法) DSA Prim 最小生成树 DSA Kruskal 最小生成树 DSA Dijkstra 最短路径算法 DSA 地图着色算法 DSA 分数背包问题 DSA 作业排序截止日期 DSA 最佳合并模式算法

动态规划

DSA 动态规划 DSA 矩阵链乘法 DSA Floyd Warshall 算法 DSA 0-1 背包问题 DSA 最长公共子序列算法 DSA 旅行商问题(动态方法)

近似算法

DSA 近似算法 DSA 顶点覆盖算法 DSA 集合覆盖问题 DSA 旅行商问题(近似方法)

随机算法

DSA 随机算法 DSA 随机快速排序算法 DSA Karger 最小割算法 DSA Fisher-Yates 洗牌算法

DSA 有用资源

DSA 问答 DSA 快速指南


二分查找算法


二分查找是一种快速的查找算法,运行时复杂度为Ο(log n)。该算法基于分治原则,在搜索之前将数组一分为二。为了使该算法正常工作,数据集合应为排序后的数据。

二分查找通过比较集合中最中间的项来查找特定的键值。如果匹配,则返回该项的索引。但如果中间项的值大于键值,则搜索中间项的右侧子数组。否则,搜索左侧子数组。此过程递归进行,直到子数组的大小减小到零。

binary_search_algorithm

二分查找算法

二分查找算法是一种区间搜索方法,仅按区间进行搜索。二分查找算法的输入必须始终位于已排序的数组中,因为它会根据较大值或较小值将数组划分为子数组。该算法遵循以下步骤 −

步骤 1 − 选择数组中的中间项,并将其与要搜索的键值进行比较。如果匹配,则返回中位数的位置。

步骤 2 − 如果与键值不匹配,则检查键值是否大于或小于中位数。

步骤 3 − 如果键值大于中位数,则在右侧子数组中执行搜索;如果键值小于中位数,则在左侧子数组中执行搜索。

步骤 4 − 重复步骤 1、2 和 3,直到子数组的大小变为 1。

步骤 5 − 如果数组中不存在键值,则算法返回搜索失败。

伪代码

二分查找算法的伪代码如下所示 −

Procedure binary_search
   A ← sorted array
   n ← size of array
   x ← value to be searched

   Set lowerBound = 1
   Set upperBound = n

   while x not found
      if upperBound < lowerBound
         EXIT: x does not exists.

      set midPoint = lowerBound + ( upperBound - lowerBound ) / 2

      if A[midPoint] < x
         set lowerBound = midPoint + 1

      if A[midPoint] > x
         set upperBound = midPoint - 1

      if A[midPoint] = x
         EXIT: x found at location midPoint
   end while
end procedure

分析

由于二分查找算法是迭代搜索,因此计算时间复杂度不如线性查找算法那么简单。

输入数组在每次迭代失败后都会被划分成多个子数组,从而进行迭代搜索。因此,形成的递归关系是一个除法函数。

简单来说,

  • 第一次迭代时,会在整个数组中搜索元素。因此,数组长度 = n。

  • 第二次迭代时,只搜索原始数组的一半。因此,数组长度 = n/2。

  • 第三次迭代时,会搜索前一个子数组的一半。这里,数组的长度 = n/4。

  • 类似地,在第 i 次迭代中,数组的长度将变为 n/2i

为了成功搜索,最后一次迭代后数组的长度必须为 1。因此,

n/2i = 1

这样我们就得到了 −

n = 2i

对两边应用对数,

log n = log 2i
log n = i. log 2
i = log n

二分查找算法的时间复杂度为 O(log n)

示例

二分查找必须对目标数组进行排序才能进行。我们将通过一个图示示例来学习二分查找的过程。以下是我们已排序的数组,假设我们需要使用二分查找来查找值 31 的位置。

binary_search_with_pictorial_example

首先,我们使用公式 − 确定数组的一半。

mid = low + (high - low) / 2

这里是 0 + (9 - 0) / 2 = 4(整数值 4.5)。因此,4 是数组的中间值。

4th_index_array

现在,我们将存储在位置 4 的值与正在搜索的值(即 31)进行比较。我们发现位置 4 的值为 27,不匹配。由于该值大于 27,并且我们有一个排序好的数组,因此我们也知道目标值必须位于数组的上半部分。

location_4_value_27

我们将低位改为中位 + 1,然后再次找到新的中位值。

低位 = 中位 + 1
中位 = 低位 + (高位 - 低位) / 2

新的中位现在是 7。我们将存储在位置 7 的值与目标值 31 进行比较。

at_loaction_7

存储在位置 7 的值不匹配,而是小于我们要查找的值。因此,该值必须位于该位置的下半部分。

location_7_not_ match

因此,我们再次计算中间值。这次是 5。

at_location_5

我们将存储在位置 5 的值与目标值进行比较。我们发现它匹配。

location_5_matched

我们得出结论,目标值 31 存储在位置 5。

二分查找将可搜索项减半,从而将比较次数减少到非常少。

实现

二分查找是一种快速搜索算法,运行时复杂度为 Ο(log n)。该搜索算法遵循分治原则。为了使该算法正常工作,数据集合应为排序形式。

#include<stdio.h>
void binary_search(int a[], int low, int high, int key){
   int mid;
   mid = (low + high) / 2;
   if (low <= high) {
      if (a[mid] == key)
         printf("Element found at index: %d
", mid);
      else if(key < a[mid])
         binary_search(a, low, mid-1, key);
      else if (a[mid] < key)
         binary_search(a, mid+1, high, key);
   } else if (low > high)
      printf("Unsuccessful Search
");
}
int main(){
   int i, n, low, high, key;
   n = 5;
   low = 0;
   high = n-1;
   int a[10] = {12, 14, 18, 22, 39};
   key = 22;
   binary_search(a, low, high, key);
   key = 23;
   binary_search(a, low, high, key);
   return 0;
}

输出

Element found at index: 3
Unsuccessful Search
#include <iostream>
using namespace std;
void binary_search(int a[], int low, int high, int key){
   int mid;
   mid = (low + high) / 2;
   if (low <= high) {
      if (a[mid] == key)
         cout << "Element found at index: " << mid << endl;
      else if(key < a[mid])
         binary_search(a, low, mid-1, key);
      else if (a[mid] < key)
         binary_search(a, mid+1, high, key);
   } else if (low > high)
      cout << "Unsuccessful Search" <<endl;
}
int main(){
   int i, n, low, high, key;
   n = 5;
   low = 0;
   high = n-1;
   int a[10] = {12, 14, 18, 22, 39};
   key = 22;
   binary_search(a, low, high, key);
   key = 23;
   binary_search(a, low, high, key);
   return 0;
}

输出

Element found at index: 3
Unsuccessful Search
import java.io.*;
import java.util.*;
public class BinarySearch {
   static void binary_search(int a[], int low, int high, int key) {
      int mid = (low + high) / 2;
      if (low <= high) {
         if (a[mid] == key)
            System.out.println("Element found at index: " + mid);
         else if(key < a[mid])
            binary_search(a, low, mid-1, key);
         else if (a[mid] < key)
            binary_search(a, mid+1, high, key);
      } else if (low > high)
         System.out.println("Unsuccessful Search");
   }
   public static void main(String args[]) {
      int n, key, low, high;
      n = 5;
      low = 0;
      high = n-1;
      int a[] = {12, 14, 18, 22, 39};
      key = 22;
      binary_search(a, low, high, key);
      key = 23;
      binary_search(a, low, high, key);
   }
}

输出

Element found at index: 3
Unsuccessful Search
def binary_search(a, low, high, key):
   mid = (low + high) // 2
   if (low <= high):
      if(a[mid] == key):
         print("The element is present at index:", mid)
      elif(key < a[mid]):
         binary_search(a, low, mid-1, key)
      elif (a[mid] < key):
         binary_search(a, mid+1, high, key)
   if(low > high):
      print("Unsuccessful Search")

a = [6, 12, 14, 18, 22, 39, 55, 182]
n = len(a)
low = 0
high = n-1
key = 22
binary_search(a, low, high, key)
key = 54
binary_search(a, low, high, key)

输出

The element is present at index: 4
Unsuccessful Search