目录
求Leftmost的元素,适用于查找时数组中元素有重复的情况
1.前提条件及流程
1.数组有序
2.i=0,j=n-1,一个指向对头,一个指向队尾。
3.如果i>j,结束查找。
4.设置mid中间索引=floor(i+j)/2,向下取整
5.如果target<Am,设置j=m-1,跳到第三步
6.如果target>Am,设置i=m+1,跳到第三步
2.查找基本思想
public static int binarysearch(int[] arr, int target) { //基础版本 左闭右闭
int i = 0, j = arr.length - 1;
while (i <= j) {//取等时还要再比较一次
int mid = (i + j) >>> 1;//防止整形溢出
if (target < arr[mid]) {
j = mid - 1;
} else if (target > arr[mid]) {
i = mid + 1;
} else {
return mid;
}
}
return -1;
}
无符号右移(>>>)的作用是当数组长度过大时造成int型长度溢出
3.代码改良
改良版1
将j的下标置为arr.length(左闭右开)
public static int binarysearch2(int[] arr, int target) {
int i = 0, j = arr.length;//j只是代表下标范围,j所指的元素不参与比较
while (i < j) {//若取等当寻找那个元素不存在时就会陷入死循环
int mid = (i + j) >>> 1;
if (target < arr[mid]) {
j = mid ;
} else if (target > arr[mid]) {
i = mid + 1;
} else {
return mid;
}
}
return -1;
}
改良版2
不在循环内比较,循环只是为了缩小边界,平均比较次数减少了,少一次if-else的判断
public static int binarysearch3(int[] arr,int target){
int i=0,j=arr.length;
while(1<j-i){//j-i表示待查找元素的个数
int mid=(i+j)>>>1;
if(target<arr[mid]){
j=mid;
}else {
i=mid;//此时不能加一,因为这个数字有可能就是要寻找的元素
}
}
if(arr[i]==target){
return i;
}else {
return -1;
}
}
4.二分查找的变式
求Leftmost的元素,适用于查找时数组中元素有重复的情况
(1)引入一个中间变量来储存下标,直到找到最左侧元素
public static int binarysearchLeftmost(int[] arr,int target){
int i = 0, j = arr.length - 1;
int candidata=-1;
while (i <= j) {
int mid = (i + j) >>> 1;
if (target < arr[mid]) {
j = mid - 1;
} else if (target > arr[mid]) {
i = mid + 1;
} else {
//记录候选位置
candidata=mid;
j=mid-1;
}
}
return candidata;
}
(2)可以将candidata优化掉,将return的值改为i,这样不仅可以查找到指定元素的位置,而且当元素不在数组中我们可以返回大于等于target的最左侧的元素,以便于之后进行增删改查的操作
public static int binarysearchLeftmost2(int[] arr,int target){
int i = 0, j = arr.length - 1;
while (i <= j) {
int mid = (i + j) >>> 1;
if (target <= arr[mid]) {
j = mid - 1;
} else if (target > arr[mid]) {
i = mid + 1;
}
}
return i;//>=target并且最靠左的元素
}
求Rightmost元素与上面的代码原理一样
public static int binarysearchRighrtmost(int[] arr,int target){
int i = 0, j = arr.length - 1;
int candidata=-1;
while (i <= j) {
int mid = (i + j) >>> 1;
if (target < arr[mid]) {
j = mid - 1;
} else if (target > arr[mid]) {
i = mid + 1;
} else {
//记录候选位置
candidata=mid;
i=mid+1;
}
}
return candidata;
}
public static int binarysearchRightmost2(int[] arr,int target){
int i = 0, j = arr.length - 1;
while (i <= j) {
int mid = (i + j) >>> 1;
if (target < arr[mid]) {
j = mid - 1;
} else if (target >= arr[mid]) {
i = mid + 1;
}
}
return i-1;//<=target并且最靠右的元素
}
5. 二分查找的复杂度分析
二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好;其缺点是要求待查表为有序表,且插入删除困难。因此,折半查找方法适用于不经常变动而查找频繁的有序列表。因为每次都是折半,可以构造一颗递归树,共log2(n)层,每层只需O(1)的时间。所以共花费O(1)*log2(n)=O(log2(n))时间。
6.例题
class Solution {
public int[] searchRange(int[] nums, int target) {
int index = Left(nums, target);
if (index == -1) {
return new int[]{-1, -1};
}
return new int[]{index, Right(nums,target)};
}
public static int Left(int[] arr,int target){
int i=0,j=arr.length-1;
int cnt=-1;
while(i<=j){
int mid=(i+j)>>>1;
if(target<arr[mid]){
j=mid-1;
} else if (target>arr[mid]) {
i=mid+1;
}else {
cnt=mid;
j=mid-1;
}
}
return cnt;
}
public static int Right(int[] arr,int target){
int i=0,j=arr.length-1;
int cnt=-1;
while(i<=j){
int mid=(i+j)>>>1;
if(target<arr[mid]){
j=mid-1;
} else if (target>arr[mid]) {
i=mid+1;
}else {
cnt=mid;
i=mid+1;
}
}
return cnt;
}
}