数据结构之二分查找

栏目: 编程工具 · 发布时间: 5年前

内容简介:本文是数据结构和算法之美的学习笔记二分查找是一种非常简单易懂的快速查找算法,生活中到处可见。比如有一个0-99之间的数组,随便取一个数,每猜一次都告诉你大了还似乎小了直到猜中为止。比如这个数是33第一次 0-99 中间数是49 49>33

本文是数据结构和算法之美的学习笔记

二分查找是一种非常简单易懂的快速查找算法,生活中到处可见。比如有一个0-99之间的数组,随便取一个数,每猜一次都告诉你大了还似乎小了直到猜中为止。比如这个数是33

第一次 0-99 中间数是49 49>33

第二次 0-48 中间数是24 24<33

第三次 25-48 中间数是36 36>33

第四次 25-35 中间数是30 30<33

第五次 31-36 中间数是33 33=33

只需要5次就找到了

二分查找针对的是一个有序的数据集合,每次都通过根区间内的中间元素对比,将待查找的区间缩小为之前的一半,知道招到查找的元素,或者区间缩小为 0。

最简单的二分查找实现:

public int bsearch(int[] a, int n, int value) {
  int low = 0;
  int high = n - 1;

  while (low <= high) {
    int mid = (low + high) / 2;
    if (a[mid] == value) {
      return mid;
    } else if (a[mid] < value) {
      low = mid + 1;
    } else {
      high = mid - 1;
    }
  }

  return -1;
}

之所以是最简单的实现,因为限制很多, 必须是有序的数组并且不能有重复的元素

循环退出条件

low<=high

mid的取值

mid=(low+high)/2这种写法有问题。因为low和high比较大的话,两者之和有可能会溢出。改进的方法是low+(high-low)/2。更近一步的话可以使用位运算符,因为计算机处理位运算符比除法快,low+((high-low)>>1)

low和high的更新

low=mid+1 heigh=mid-1 如果直接写low=mid 或者 high=mid可能会发生死循环。比如high=3,low=3,如果a[3]不等于value,就死循环了。

二分查找除了使用循环实现,还可以使用递归,因为其子任务都是找到中间的值跟要找的值做比较

// 二分查找的递归实现
public int bsearch(int[] a, int n, int val) {
  return bsearchInternally(a, 0, n - 1, val);
}

private int bsearchInternally(int[] a, int low, int high, int value) {
  if (low > high) return -1;

  int mid =  low + ((high - low) >> 1);
  if (a[mid] == value) {
    return mid;
  } else if (a[mid] < value) {
    return bsearchInternally(a, mid+1, high, value);
  } else {
    return bsearchInternally(a, low, mid-1, value);
  }
}

二分查找的局限性

(1)二分查找依赖的是顺序表结构,简单点就是数组。不能依赖链表,主要原因是二分查找算法需要按照下标随机访问元素。我们知道,按照下标随机访问数据的时候,链表比数组耗时,也会导致二分查找的时间复杂度高。

(2)二分查找针对的是有序的数据

如果不是有序的,必须得先 排序 在查找。二分查找只能用在插入和删除操作不频繁,一次排序多次查找的场景中,或者直接就是静态的数据,对于动态变化的数据集,二分查找并不适用。

(3)数据量太小的话不适合二分查找

数据量太小的话直接遍历就行了,不过如果比较的时候比较耗时,这时候还是用二分查找比较好,可以减少比较的次数。

(4)数据量太大也不适合二分查找

因为二分查找底层需要依赖数组这种数据结构,而数组为了支持随机访问,对内存的连续性要求比较高。如果数组都存不下了那也就没有查找了。

思考:使用二分法求一个数的平方根,精确到小数点后6位

//x位输入的数 precision为精确度比如0.000001
public double sqrt(double x, double precision) {
        if(x == 0 || x == 1){
            return x;
        }
        double low = 0;
        double high = x;
        double mid = low + (high - low)/2;
        while(Math.abs(high - low )> precision) {
            if (mid * mid > x ) {
                high = mid;
            } else if (mid * mid < x) {
                low = mid;
            } else {
                return mid;
            }
            mid = low + (high - low)/2;
        }
        return mid;
    }

上面的二分查找是最简单的二分查找,数据中没有重复的数据,如果数组中有重复的数据,使用上面的二分查找就可能会出错了

比如查找第一个等于给定值的元素,使用下面一组数据 1,2,3,4,5,8,8,8,11,16

如果使用上面的查找方法,我们找到的是第三个8,而正确的结果应该是第一个8。这时候可以改造一下代码

public int bsearch(int[] a, int n, int value) {
  int low = 0;
  int high = n - 1;
  while (low <= high) {
    int mid =  low + ((high - low) >> 1);
    if (a[mid] > value) {
      high = mid - 1;
    } else if (a[mid] < value) {
      low = mid + 1;
    } else {
      if ((mid == 0) || (a[mid - 1] != value)) return mid;
      else high = mid - 1;
    }
  }
  return -1;
}

当a[mid]等于要查找的元素的时候,需要判断一下是不是第一个给定的值,如果mid为0那他已经是数组的第一个元素,肯定是我们要找的,如果不为0但是它前面的一个元素不等于value也是我们要找的,如果一样那说明不是我们要找的更新high值为high=mid-1

查找第一个大于等于给定值的元素

public int bsearch(int[] a, int n, int value) {
  int low = 0;
  int high = n - 1;
  while (low <= high) {
    int mid =  low + ((high - low) >> 1);
    if (a[mid] >= value) {
      if ((mid == 0) || (a[mid - 1] < value)) return mid;
      else high = mid - 1;
    } else {
      low = mid + 1;
    }
  }
  return -1;
}

如果a[mid]小于小于要找的value,那么值肯定在mid+1到high之间

如果a[mid]大于要查找的值,就看看它是不是第一个元素,或者它前面的元素是不是大于等于value值


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

正当法律程序简史

正当法律程序简史

(美)约翰·V.奥尔特 / 杨明成、陈霜玲 / 商务印书馆 / 2006-8 / 14.00元

本书的主题——正当法律程序,是英美法的核心概念,它使诸如法治、经济自由、个人自治以及免于政府专断行为的侵害等价值观念具体化,因而是法学领域一个永恒的主题,数百年以来一直是法学家、法官及律师关注的重点。本书以极为简洁、精确的语言总结了五百年法律发展的恢弘历史,为人们描述了正当法律程序观念发展演变的清晰轨迹。而沿着这条轨迹,人们可以准确地了解正当法律程序这一重要概念所包含的广泛的问题。 作为一本......一起来看看 《正当法律程序简史》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具