目录
一、什么二分查找
二分查找也称为折半查找,是一种在有序数组中查找特定元素的高效算法。其基本思想是将目标元素与数组中间位置的元素进行比较,如果目标元素等于中间元素,则查找成功;如果目标元素小于中间元素,则在数组的前半部分继续查找;如果目标元素大于中间元素,则在数组的后半部分继续查找。重复这个过程,直到找到目标元素或者确定目标元素不在数组中。
例如,在有序数组 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 中查找数字 7。首先取中间元素 5,由于 7 大于 5,所以在数组的后半部分继续查找。再次取中间元素 8,7 小于 8,所以在 [6, 7, 8, 9, 10] 的前半部分继续查找,即 [6, 7],取中间元素 6,7 大于 6,所以确定目标元素为 7。
二、二分查找的作用
- 高效查找:对于大规模的有序数据集合,二分查找可以快速定位目标元素,时间复杂度为 O (log n),其中 n 是数组的长度。相比之下,顺序查找的时间复杂度为 O (n),在数据量大时效率明显低于二分查找。 就好比中国14亿人口,如果按照某种方式进行排序来进行查找,只需要查找32次,就可以特定的找到这个人的信息。
- 节省资源:由于查找速度快,可以减少不必要的计算和内存访问,从而节省计算资源和时间。
- 应用广泛:在许多领域都有应用,如数据库查询、排序算法、数值计算等。
- c代码
#include <stdio.h> int binarySearch(int arr[], int n, int target) { int left = 0; int right = n - 1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) { return mid; } else if (arr[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; } int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int n = sizeof(arr) / sizeof(arr[0]); int target = 7; int index = binarySearch(arr, n, target); if (index!= -1) { printf("目标元素 %d 在数组中的位置是:%d\n", target, index); } else { printf("目标元素 %d 不在数组中\n", target); } return 0; }
三、什么是非顺序二分查找
非顺序二分查找是在不完全有序的数组中进行查找的方法。
通常情况下,二分查找要求数组是完全有序的,但在某些情况下,数组可能只是部分有序或者存在一定的规律。非顺序二分查找需要根据具体的情况设计合适的查找策略。
例如,数组中可能存在一些重复元素,或者数组的一部分是有序的,另一部分是无序的。在这种情况下,可以利用已知的部分有序性或者其他特征来进行查找。
那么整体来说非顺序二分查找其实就是在一个数组中找到一部分并且有序的数组,那么通过这一部分来进行二分的思想就能确定出要求出的结果。
四、实战非顺序二分查找
二分查找:
思路和算法
对于有序数组,可以使用二分查找的方法查找元素。
但是这道题中,数组本身不是有序的,进行旋转后只保证了数组的局部是有序的,这还能进行二分查找吗?答案是可以的。
可以发现的是,我们将数组从中间分开成左右两部分的时候,一定有一部分的数组是有序的。拿示例来看,我们从 6 这个位置分开以后数组变成了 [4, 5, 6] 和 [7, 0, 1, 2] 两个部分,其中左边 [4, 5, 6] 这个部分的数组是有序的,其他也是如此。
这启示我们可以在常规二分查找的时候查看当前 mid 为分割位置分割出来的两个部分 [l, mid] 和 [mid + 1, r] 哪个部分是有序的,并根据有序的那个部分确定我们该如何改变二分查找的上下界,因为我们能够根据有序的那部分判断出 target 在不在这个部分:
如果 [l, mid - 1] 是有序数组,且 target 的大小满足 [nums[l],nums[mid]),则我们应该将搜索范围缩小至 [l, mid - 1],否则在 [mid + 1, r] 中寻找。
如果 [mid, r] 是有序数组,且 target 的大小满足 (nums[mid+1],nums[r]],则我们应该将搜索范围缩小至 [mid + 1, r],否则在 [l, mid - 1] 中寻找。
需要注意的是,二分的写法有很多种,所以在判断 target
大小与有序部分的关系的时候可能会出现细节上的差别。
class Solution {
public:
int search(vector<int>& nums, int target) {
int n = (int)nums.size();
if (!n) {
return -1;
}
if (n == 1) {
return nums[0] == target ? 0 : -1;
}
int l = 0, r = n - 1;
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] == target) return mid;
if (nums[0] <= nums[mid]) {
if (nums[0] <= target && target < nums[mid]) {
r = mid - 1;
} else {
l = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[n - 1]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
};
复杂度分析
时间复杂度:
O(logn),其中 n 为 nums 数组的大小。整个算法时间复杂度即为二分查找的时间复杂度 O(logn)。
空间复杂度:
O(1) 。我们只需要常数级别的空间存放变量。