4.10二分查找

二分查找下

1.通过ip查找ip归属地

数据库存储的是ip区间和归属地按对储存

2.二分查找变形四个问题

二分查找上是最简单的一种情况,没有重复值出现,现在有重复值出现的二分查找,这里关键点还是一样,先找到相等的目标值,再判断是否是我要的第一个或者最后一个等值的目标值

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

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;

}

注:找到同值,且左边没有等值,或者mid到达左边极端点,则输出,否则前面有同值元素存在则继续更新左区间

ii.查找最后一个等于给定值的元素

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 == n - 1) || (a[mid + 1] != value)) return mid;

      else low = mid + 1;

    }

  }

  return -1;

}

注:找到同值,且右边没有等值,或者到达右边极端直接输出,否则后面有等值,更新右区间

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

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;

}

注:这里要求大于等于,先找到符合条件的数值,然后判断是否是第一个出现的,即左区间

iiii.查找最后一个小于等于给定值的元素

public int bsearch7(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 ((mid == n - 1) || (a[mid + 1] > value)) return mid;

      else low = mid + 1;

    }

  }

  return -1;

}

注:这里要求小于等于,先找到符合条件的数值,再判断是不是最后一个出现的,即右区间

3.解决开篇问题ip归属地

IP 地址可以转化为 32 位的整型数。所以,我们可以将起始地址,按照对应的整型值的大小关系,从小到大进行排序。

先通过二分查找,找到最后一个起始 IP 小于等于这个 IP 的 IP 区间,然后,检查这个 IP 是否在这个 IP 区间内,如果在,我们就取出对应的归属地显示;如果不在,就返回未查找到。

4.课后思考题lc33,循环有序数组查找

一个循环有序数组(仅部分区间是有序的,不能要求先排序),比如 4,5,6,1,2,3。针对这种情况,如何实现一个求“值等于给定值”的二分查找算法呢?

解:

i.找到分界下标,拆成两个有序数组

ii.判断目标值所在哪个区间,然后区间上二分查找

总结:

i.二分查找,应用场景是数学中寻找近似值(区间表示),判断答案所在区间(近似值)

ii.关键都是目标值和待检验值三种情况分类讨论:大于,小于,等于,来找到数值满足的元素

iii.再去判断是否满足时间要求:第一个出现,最后一个出现(这里简单写法是先找到符合条件数值,然后因为满足正确条件更少,所以先写符合正确结果的数值,输出如if(n==0||a[n-1]!=a[n]))

iiii.绝大多数二分查找能做的一般都用二叉查找树和散列表实现,虽然二分查找更省空间,但空间廉价时,没必要了,故二分查找专用来找到近似值和区间范围,散列表是精确查找

跳表

1.为什么Redis一定要用跳表来实现有序集合?

2.回顾上篇,开启下文

二分查找底层依赖是数组的随机访问特定,如果数据存储在链表中,只需要对链表稍加改造,就可以支持类似二分的查找算法,这种链表叫跳表

3.跳表

跳表是一种动态数据结构,支持快速的插入删除查找操作,Redis采用跳表来实现有序集合,不使用同样红黑树。借鉴二分查找,近似区间的思想,

给单链表加上多级索引表,每隔两个元素建一个对应的多级索引链表,实现时间复杂度o(logn),空间复杂度o(n),插入删除o(logn)

索引表实现了区间分区功能(近似思想),动态插入,红黑树平衡树都是通过左右旋转保持左右子树大小平衡,跳表插入数据时也选择同时插入到索引层

4.问题解答

插入、删除、查找以及迭代输出有序序列这几个操作,红黑树也可以完成,时间复杂度跟跳表是一样的。但是,按照区间来查找数据(二分查找近似思想)这个操作,红黑树的效率没有跳表高。跳表比红黑树,比起来更简单,好写好懂,更灵活(通过更改索引策略)唯一缺点是没有红黑树成熟,现成的跳表需要自己实现

你可能感兴趣的:(4.10二分查找)