使用二分查找统计数组中与目标数相同的数的个数
该算法思想是基于二分查找数基础上进行改进,目的是统计数组中有多少个数与目标数相等。
注:
- 当直接使用for循环去遍历数组,每次遍历都比对一次,时间复杂度将会是O(n)
- 如果使用一次二分查找,首先找到一个与目标数相等数的下标后,使用while以这个下标为中心,向左或向右去比较。如果相同的数只有2个的话,那么向左向右搜寻一次就可以得出结果,但如果一个数组中的所有数都相同,那么还是需要比对n次,按这个思想查找的时间复杂度也是O(n)。
- 这里我采用的思想是 先使用一次二分查找找到这个数的左边界,然后在找到左边界的情况下再使用一次二分查找找到数的右边界,右边界与左边界之差就是数组中这个数的个数。 该算法的时间复杂度和普通二分算法的时间复杂度是一样的。都是O(lgN)。
import java.util.Scanner;
public class BinarySearch_findSameNum {
public static void main(String[] args) {
int arr[] = {1, 1, 5, 5, 5, 8, 8, 9, 10, 15, 15}; //定义一个有序的二分数组
int target = new Scanner(System.in).nextInt(); //输入查找数
left(arr, 0, arr.length, target); //第一次二分 查找左边界
}
//查找左边界的函数left
private static void left(int[] arr, int low, int high, int target) {
while (low <= high) { //定义while循环的结束条件 当下标low>high时跳出while循环
int middle = (low + high) / 2;//注意 这里的middle计算方式会有很大的影响 我还是采用的相加取中值
if (arr[middle] >= target) {
//这里要找左边界,因此当arr[middle]大于或者等于target时,则证明左边界在low~middle-1之间
//注意 二分查找数时,当arr[middle]==target时就找到要查找的结果了,但这里还不能结束。
// 因为目标数在数组中有2个或2个以上时,找到的这个索引很大可能不是最左边的,还需要进行循环,直到找到左边界
high = middle - 1; //改变上标,继续遍历
} else {
low = middle + 1;
}
}//当不断在while循环中遍历时,low的值一定会大于high
//判断最后一次循环结束的下标low对应的数组值是否与target相等
if (arr[low] == target && low < arr.length) {
System.out.println("left search success:" + low);
//相等,则左边界存在。先获得目标数的左边界low
int right = right(arr, low+1, arr.length - 1, target); //在调用寻找有边界的函数right
//注意 这里传给右边界函数的下标和上标与找左边界时有所变化 因为我们已经找到了左边界,所有可以直接将数组左边界右边的数传给函数
//下标可以取low+1
//这里的上标为arr.length - 1 数组的长度减1 !!!!这个-1非常重要,对应着middle的计算方式。
//因为我是直接上下标相加除2的 就拿本题为列。找到查找右边界的函数。如要查找15 当找到左边界是9时 再将上标11传过去 middle计算得10
// arr[10]=15确实等于target,这时会将下标low加1。low=11后就直接会数组越界 所以将arr.length-1传过去 上标为10,就不会出现下标+1后越界
System.out.println("数组中与" + target + "相同的有:" + (right - low + 1) + "个");
} else {
System.out.println("error");
}
}
//查找右边界的函数right
private static int right(int[] arr, int low, int high, int target) {
//查找右边界时就很简单了,思想与查找左边界一致
while (low <= high) {
int middle = (high + low) / 2;
if (arr[middle] <= target) {
//如果arr[middle] <= target 就一直改变下标的值,直到low>high跳出while循环
low = middle + 1;
} else {
high = middle - 1;
}
}
if (arr[high] == target && high < arr.length) {
System.out.println("right search find:" + high);
return high; //返回右边界的索引
}
}
}
闲来无聊写的,若有错误的地方还请各位大佬指正