二分查找也称折半查找,它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
二分查找充分利用了元素间的次序关系,采用分冶策略,可在最坏的情况下用O(logn)完成搜索任务。
1. 如果待查序列为空,那么就返回-1,并退出算法;这表示查找不到目标元素。
2. 如果待查询序列不为空,则将它的中间元素与查找的目标元素进行匹配,看它们是否相等。
3. 如果相等,则返回该中间元素的索引,并退出算法;此时就查找成功。
4. 如果不相等,就再比较这两个元素的大小。
5. 如果该中间元素大于目标元素,那么就将当前序列的前半部分作为新的待查序列。
6. 如果该中间元素小于目标元素,那么就将当前序列的后半部分作为新的待查序列。
二分之查找之所有快速,是因为它在匹配不成功的时候,每次都能排除剩余元素中一半的元素。因此可能包含目标元素的有效范围就收缩得很块,而不像顺序查找那样,每次仅能排除一个元素。
二分查找原理摘自博客:https://blog.csdn.net/m0_46937429/article/details/114947535
1~99整数之间随意取一个正确答案,你来猜这个数,我来告诉你比正确答案大还是小。
为了最快找到正确答案一般来说我们会取中位数50作为第一次比较的数;
如果比50大,我们会取51~99的中位数75来做第二次比较;如果比50小,我们会取1~49的中位数25来做第二次比较;依此类推,直到找到正确答案。
来源:力扣(leetCode)第35题:搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例:
示例 1:
输入: [1,3,5,6], 5
输出: 2
示例2:
输入: [1,3,5,6], 2
输出: 1
示例 3:
输入: [1,3,5,6], 7
输出: 4
示例 4:
输入: [1,3,5,6], 0
输出: 0
投机写了个简单的解题方案:
private static int searchInsert(int[] nums, int target) {
int i = 0;
for (int num : nums) {
if (target == num || target < num) {
return i;
}
i++;
}
return i;
}
测试:
public static void main(String[] args) {
int[] nums = new int[]{1,3,5,6};
System.out.println("target = 5 result:" + searchInsert(nums, 5));
System.out.println("target = 5 result:" + searchInsert(nums, 2));
System.out.println("target = 5 result:" + searchInsert(nums, 7));
System.out.println("target = 5 result:" + searchInsert(nums, 0));
}
输出:
target = 5 result:2
target = 5 result:1
target = 5 result:4
target = 5 result:0
与预期结果一致^^,在leetCode上也是通过了所有测试用例,执行用时: 0 ms 内存消耗: 38.2 MB。
private static int binarySearchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
//处理左右边界
if (target < nums[left]) {
return 0;
}
if (target > nums[right]) {
return nums.length;
}
//二分查找
while (left <= right) {
int mid = left + (right - left) / 2;
//目标值在数组中,直接返回下标
if (nums[mid] == target) {
return mid;
} else if (nums[mid] < target) {
//目标值在 nums[mid] 右边时判断是否比下标 mid 的后一位小
if (target < nums[mid + 1]) {
//满足 则说明该值在下标 mid 与 mid + 1 之间 直接返回下标mid + 1
return mid + 1;
}
left = mid + 1;
} else if (nums[mid] > target) {
//目标值在 nums[mid] 左边时判断是否比下标 mid 的前一位大
if (target > nums[mid - 1]) {
//满足 则说明该值在下标 mid - 1 与 mid 之间 直接返回下标mid(其实应该是 mid-1+1 的 也就是mid了)
return mid;
}
right = mid - 1;
}
}
return -1;
}
1. 首先要处理左右边界情况,也就是 target 小于 nums 最小值或大于最大值的情况
2. 使用二分查找法来找 target 在 nums 中的下标,如果找到则直接返回数组下标
3. 如果 target 在 nums[mid] 右边时,判断 target 是否比下标为 mid 的后一位小;
满足:则说明该值在下标 mid 与 mid + 1 之间 直接返回下标 mid + 1;
不满足:则 left = mid + 1 继续查找
4. 如果 target 在 nums[mid] 左边时,判断 target 是否比下标为 mid 的前一位大
满足: 则说明该值在下标 mid - 1 与 mid 之间 直接返回下标 mid(其实应该是 mid-1+1 的 也就是mid了)
不满足:则 right = mid - 1 继续查找
建议使用这种写法:mid = left + (right - left) / 2。
对于mid = (right + left) / 2 写法来说,由于left+right很容易超过int范围,所以容易溢出。