二分查找

原理

对于一个已经排序的数组,我们可以使用二分查找,在O(logN)的时间内找到特定的元素

具体的步骤如图:

比如,如果我们想找到6这个数

  1. 我们可以先找到8,发现8比6大
  2. 往左边走,在左半部分,找到中间位置,是3,发现3比6小
  3. 往右边走,在8和3之间,找到了6

如果查找56,则第二步向右移动

我们每次都和候选空间的中间位置做比较,每次的步长都是一半的区域,所以时间复杂度是 O(logN)

代码

非递归

int bsearch(int* arr, int target, int left, int right) {
  if (arr == nullptr) {
    return -1;
  }

  while (left <= right) {//这里的运算符是 <=
    int mid = left + (right - left) / 2; // 这里不用 (left+right)/2,可以防止整数溢出
    if (arr[mid] == target) {
      return mid;
    } else if (arr[mid] > target) {
      right = mid - 1;
    } else {
      left = mid + 1;
    }
  }

  return -1;
}

递归

int bsearch(int* arr, int target, int left, int right) {
  if (arr == nullptr || left > right) {
    return -1;
  }

  int mid = left + (right - left) / 2;
  if (arr[mid] == target) {
    return mid;
  } else if (arr[mid] > target) {
    return bsearch(arr, target, left, mid - 1);
  } else {
    return bsearch(arr, target, mid + 1, right);
  }

  return -1;
}

变形

旋转后的排序数组

如果一个数组定义为这样:

int arr[] = {33, 56, 78, 98, 1, 2, 3, 6, 7, 8, 9, 22};

这个排序数组,做了一个旋转,乍一看,不能使用二分查找,其实,不能被这个调整给吓到了,这里也是有规律的,只是稍微麻烦点而已。不要怕麻烦。

对于 {33, 56, 78, 98, 1, 2, 3, 6, 7, 8, 9, 22},中间位置的数小于最右边的数,说明右半部分是有序的,如果要找的数正落在右半部分,则可以使用二分查找了,如果没有落在右半部分,可以递归。

同理,对于{8, 9, 22, 33, 56, 78, 98, 1, 2, 3, 6, 7},中间位置的数大于最左边的数,说明左半部分是有序的,可以分情况二分查找,或者递归。

所以,这里的代码为:

#include <iostream>

int bsearch(int* arr, int target, int left, int right) {
  if (arr == nullptr || left > right) {
    return -1;
  }

  int mid = left + (right - left) / 2;
  if (arr[mid] == target) {
    return mid;
  } else if (arr[mid] > target) {
    return bsearch(arr, target, left, mid - 1);
  } else {
    return bsearch(arr, target, mid + 1, right);
  }

  return -1;
}

int func(int* arr, int target, int left, int right) {
  if (arr == nullptr || left > right) {
    return -1;
  }

  int mid = left + (right - left) / 2;
  if (arr[mid] == target) {
    return mid;
  } else if (arr[mid] > arr[left]) {
    if (arr[mid] > target && target >= arr[left]) {
      return bsearch(arr, target, left, mid - 1);
    } else {
      return func(arr, target, mid + 1, right);
    }
  } else {
    if (arr[mid] < target && target <= arr[right]) {
      return bsearch(arr, target, mid + 1, right);
    } else {
      return func(arr, target, left, mid - 1);
    }
  }

  return -1;
}

void test1() {
  int arr[] = {8, 9, 22, 33, 56, 78, 98, 1, 2, 3, 6, 7};
  int len = sizeof(arr) / sizeof(int);

  printf("%d, %d, %d, %d", func(arr, 8, 0, len - 1) == 0,
         func(arr, 7, 0, len - 1) == len - 1, func(arr, 98, 0, len - 1) == 6,
         func(arr, 22, 0, len - 1) == 2);
}

int main() {
  test1();

  return 0;
}
posted @ 2020-05-26 22:59  二狗啸地  阅读(93)  评论(0)    收藏  举报