一、二分搜索
Given a sorted integer array- nums, and anteger - target.
Find the any/first/last position of target in nums
Return -1 if target does not exist.
算法复杂度: O(logn)
关键点:
start + 1 < end
start + (end - start)/2
A[mid] =
class Solution{
/**
* @param nums: The integer array
* @param target: target to find.
* @return: The first position of target. Position start from 0
*/
public int binarySearch(int[] nums, int target) {
if(nums == null || nums.length = 0 ){
return -1;
}
int start = 0, end = nums.length -1;
while(start + 1 < end) {
int mid = start + (end - start)/2;
if(nums[mid] == target) {
end = mid;
} else if(nums[mid] < target) {
start = mid;
}else {
end = mid;
}
}
if(nums[start] == target){
return start;
}
if(nums[end] == target){
return end;
}
return -1;
}
}
变种题
二分位置之OOXX
一般会给你一个数组,让你找数组中第一个/最后一个满足某个条件的位置
OOOOO.... OOXX.... XXXXXX
题目:
描述
代码库的版本号是从 1
到 n
的整数。某一天,有人提交了错误版本的代码,因此造成自身及之后版本的代码在单元测试中均出错。请找出第一个错误的版本号。
你可以通过 isBadVersion
的接口来判断版本号 version 是否在单元测试中出错,具体接口详情和调用方法请见代码的注释部分。
调用 isBadVersion 的方法,比如java的调用方式是SVNRepo.isBadVersion(v)
class Solution {
/**
* @param n : An integers
* @return: An integer which is the first bad version
*/
public it findFirstBadVersion(int n ) {
int start = 1, end = n;
while(start + 1 < end){
int mid = start + (end -start) /2;
if(SVNRepo.isBadVersion(mid)) {
end = mid;
} else {
start = end;
}
}
if(SVNRepo.isBadVersion(satrt)) {
return start;
}
return end;
}
}
题目:
描述
假设一个按升序排好序的数组在其某一未知点发生了旋转(比如0 1 2 4 5 6 7
可能变成4 5 6 7 0 1 2
)。你需要找到其中最小的元素。
第一步先看最后一个数是否大雨第一个数,如果大于说明这个数组并没有翻转。直接返回第一个数
第二步做二分,通过和第一个数比较来决定向左或者移动:
如果大于或者等于第一个数说明在翻转的左边,那么向右移动
同理,如果小于则在右边,向左移动
public class Solution {
/**
* @param nums: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] nums) {
if(numd == null || nums.length == 0) {
return -1;
}
//如果最后一个数>=第一个数,说明没有翻转,直接返回第一个数
if(nums[nums.length-1] >= nums[0])
return nums[0]
//通过判断属于rotation的左边哪或者右边来决定 往左或者往右搜索
int start = 0, end = nums.lenghth -1;
while(start + 1< end) {
int mid = start + (end - start) /2;
if(nums[mid] >= nums[0]){
start = mid;
}else{
end = mid;
}
}
return Math.min(nums[start], nums[end]);
}
二、
二分位置 之Half half
并无法找到一个条件,形成OOXX的密性,但可以根据判断,保留下有解的那一半或者去掉无解的一半
方法:二分
二分查找第一个不小于target的元素很简单,但是需要确定二分区间的范围。
此时还是需要成倍地找到右边界。
初始右边界为1,如果右边界的数小于target,就将其倍增, 直到右边界不小于target。
这是可以二分查找了。
public class Solution {
public int searchBigSortedArray(ArrayReader reader, int target) {
int firstElement = reader.get(0);
if(firstElement == target)
return 0;
else if(firstElement > target)
return -1;
int idx = 0, jump =1;
while(jump != 0 ){
while(jump!=0 && reader.get(idx+jump) >= target) {
jump >> 1;
}
idx += jump;
jump <<= 1;
}
if(reader.get(idx + 1) == target)
return idx + 1;
else
return -1;
}
}
public class Solution {
public int searchBigSortedArray( ArrayReader reader, int target) {
int l = 0, r = 1, mid;
while(reader.get(r) < target) {
r <<= 1
}
while(l < r) {
mid = (l+r) >> 1;
if(reader.get(mid) >= target) {
r = mid;
}else {
l = mid + 1;
}
}
if(reader.get(l) == target) {
return l;
}else {
return -1;
}
}
}
159 · 寻找旋转排序数组中的最小值
描述
假设一个按升序排好序的数组在其某一未知点发生了旋转(比如0 1 2 4 5 6 7
可能变成4 5 6 7 0 1 2
)。你需要找到其中最小的元素。
样例
样例 1:
输入:[4, 5, 6, 7, 0, 1, 2]
输出:0
解释:
数组中的最小值为0
样例 2:
输入:[2,1]
输出:1
解释:
数组中的最小值为1
第一步先看最后一个数是否大雨第一个数,如果大于说明这个数组并没有翻转。直接返回第一个数
第二步做二分,通过和第一个数比较来决定向左或者移动:
如果大于或者等于第一个数说明在翻转的左边,那么向右移动
同理,如果小于则在右边,向左移动
public class Solution {
/**
* @param nums: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] nums) {
if(numd == null || nums.length == 0) {
return -1;
}
//如果最后一个数>=第一个数,说明没有翻转,直接返回第一个数
if(nums[nums.length-1] >= nums[0])
return nums[0]
//通过判断属于rotation的左边哪或者右边来决定 往左或者往右搜索
int start = 0, end = nums.lenghth -1;
while(start + 1< end) {
int mid = start + (end - start) /2;
if(nums[mid] >= nums[0]){
start = mid;
}else{
end = mid;
}
}
return Math.min(nums[start], nums[end]);
}
}
1)通过判断mid和target是否处于翻转的同一边,来决定搜索的方向
如果在同一边,按照正常的二分搜索方向
如果在翻转的不同边,反转二分搜索的方向
2)如果>最后一个数:在左边, 否则在右边
public class Solution {
/**
* @param A: an integer rotated sorted array
* @param target: an integer to be searched
* @return: an integer
*/
public int search(int[] A, int target) {
if(A == null || A.length == 0) {
returnœ -1;
}
//通过判断mid和target是否处于翻转的同一边,来决定搜索的方向
//如果 > 最后一个数: 在左边,otherwise 在右边
int start = 0, end = A.length -1, div = A[A.length -1];
while(start + 1 < end) {
int mid = start + (end - start)/2;
if(A[mid] == target) {
return mid;
}
//如果在同一边,按照正常的二分搜索方向
if((A[mid] > div && target > div)
|| A[mid] <= div && target <= div){
if(A[mid] > target) {
end = mid;
}else{
start = mid;
}
}else {
//如果在翻转的不同边,反转二分搜索的方向
if(A[mid]>target) {
start = mid
}else {
end = mid;
}
}
}
if(A[start] == target)
return start;
if(A[end] == target) {
return end;
}
return -1;
}
}