二分查找算法详解

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

二分查找算法详解

二分查找算法,是一种在有序数组中查找某一特定元素的搜索算法。

注意两点:

(1)有序:查找之前元素必须是有序的,可以是数字值有序,也可以是字典序。为什么必须有序呢?如果部分有序或循环有序可以吗?

(2)数组:所有逻辑相邻的元素在物理存储上也是相邻的,确保可以随机存取。

算法思想:

搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。

这里我们可以看到:

(1) 如果查找值和中间值不相等的时候,我们可以确保可以下次的搜索范围可以缩小一半,正是由于所有元素都是有序的这一先决条件

(2) 我们每次查找的范围都是理应包含查找值的区间,当搜索停止时,如果仍未查找到,那么此时的搜索位置就应该是查找值应该处于的位置,只是该值不在数组中而已算法实现及各种变形:

1. 非降序数组A, 查找任一个值==val的元素,若找到则返回下标位置,若未找到则返回-1

2. 非降序数组A, 查找第一个值==val的元素,若找到则返回下标位置,若未找到则返回-1 (类似:查找数组中元素最后一个小于val 值的位置)

3. 非降序数组A, 查找最后一个值==val的元素,若找到则返回下标位置,若未找到则返回-1 (类似:查找数组中元素第一个大于val 值的位置)

4. 非降序数组A, 查找任一值为val的元素,保证插入该元素后数组仍然有序,返回可以插入的任一位置

5. 非降序数组A, 查找任一值为val的元素,保证插入该元素后数组仍然有序,返回可以插入的第一个位置

6. 非降序数组A, 查找任一值为val的元素,保证插入该元素后数组仍然有序,返回可以插入的最后一个位置

7. 非降序数组A, 查找任一个值==val的元素,若找到则返回一组下标区间(该区间所有值==val),若未找到则返回-1

8. 非降序字符串数组A, 查找任一个值==val的元素,若找到则返回下标位置,若未找到则返回-1(类似:未找到时返回应该插入点)

9. 循环有序数组中查找== val 的元素,若找到则返回下标位置,若未找到则返回-1

1. 非降序数组A, 查找任一个值==val的元素,若找到则返回下标位置,若未找到则返回-1

1 int binary_search(int* a, int len, int val)

2 {

3 assert(a != NULL && len > 0);

4 int low = 0;

5 int high = len - 1;

6 while (low <= high) {

7 int mid = low + (high - low) / 2;

8 if (val < a[mid]) {

9 high = mid - 1;

10 } else if (val > a[mid]) {

11 low = mid + 1;

12 } else {

13 return mid;

14 }

15 }

16 return -1;

17 }

注意:

(1) 使用assert对函数输入进行合法性检查

(2) while 循环的条件是low<=high,这里如果查找值未找到,则此时一定low = high + 1

(3) 对val 和a[mid] 做比较时,首先考虑不等情况,最后考虑相等情况,如果随机分布的话不等的概率肯定大于相等的概率

2. 非降序数组A, 查找第一个值==val的元素,若找到则返回下标位置,若未找到则返回-1 (类似:查找数组中元素最后一个小于val 值的位置)

因为数组中可能有重复元素,所以数组中是有可能存在多个值与val 相等的,我们对普通二分进行变形:

当val < a[mid] 时,接下来的搜索范围减半 high = mid - 1

当val > a[mid] 时,接下来的搜索范围减半 low = mid + 1

当val == a[mid] 时,这个时候就不能简单的返回了,我们要求的是第一个== val 的值,什么条件下是第一个呢?

当mid == 0 那当然是第一个

当mid > 1 && a[mid - 1] != val 这个时候也是第一个

其他情况下,这个时候查找到的值不是第一个,此时我们应该继续搜索,而不是返回,搜索范围是什么呢?因为是查找第一个,那么接下来肯定应该在

此时位置的左边继续搜索,即high = mid - 1

1 int search_first(int* a, int len, int val)

2 {

3 assert(a != NULL && len > 1);

4 int low = 0;

5 int high = len - 1;

6 while (low <= high) {

7 int mid = low + (high - low) / 2;

8 if (val < a[mid]) {

9 high = mid - 1;

10 } else if (val > a[mid]) {

11 low = mid + 1;

12 } else {

13 if (mid == 0) return mid;

14 if (mid > 0 && a[mid-1] != val) return mid;

15 high = mid - 1;

16 }

17 }

18 return -1;

19 }

3. 非降序数组A, 查找最后一个值==val的元素,若找到则返回下标位置,若未找到则返回-1 (类似:查找数组中元素第一个大于val 值的位置)

算法思想与第2题相同

1 int search_last(int* a, int len, int val)

2 {

3 assert(a != NULL && len > 1);

4 int low = 0;

5 int high = len - 1;

6 while (low <= high) {

7 int mid = low + (high - low) / 2;

8 if (val < a[mid]) {

9 high = mid - 1;

10 } else if (val > a[mid]) {

11 low = mid + 1;

12 } else {

13 if (mid == (len - 1)) return mid;

14 if (mid < (len - 1) && a[mid+1] != val) return mid;

15 low = mid + 1;

16 }

17 }

18 return -1;

19 }

4. 非降序数组A, 查找任一值为val的元素,保证插入该元素后数组仍然有序,返回可以插入的任一位置

当a[mid] == val 则返回mid,因为在该位置插入val 数组一定保证有序

当循环结束后仍未查找到val值,我们之前说过,此时一定有high = low + 1,其实查找值永远都应该在low和high组成的区间内,现在区间内没空位了,所以可以宣告该值没有查找到,

如果仍然有空位,则val一定在该区间内。也就是说此时的low 和high 这两个值就是val 应该处于的位置,因为通常都是在位置之前插入,所以此时直接返回low 即可

1 int insert(int* a, int len, int val)

2 {

3 assert(a != NULL && len > 0);

4 int low = 0;

5 int high = len - 1;

6 while (low <= high) {

7 int mid = low + (high - low) / 2;

8 if (val < a[mid]) {

相关文档
最新文档