一. 二分搜索(Binary Search)模板
public class Solution {
/*
* @param nums: An integer array sorted in ascending order
* @param target: An integer
* @return: An integer
*/
public int findPosition(int[] nums, int target) {
if(nums == null || nums.length == 0){
return -1;
}
int left = 0;
int right = nums.length - 1;
int mid = 0;
while(left + 1 < right){
mid = left + (right - left)/2;
if(nums[mid] == target){
return mid;
}else if(nums[mid] < target){
left = mid;
}else{
right = mid;
}
}
if(nums[left] == target){
return left;
}
if(nums[right] == target){
return right;
}
return -1;
}
}
注意事项:
1). start + 1 < end (最后会剩下start, end两项);
2). mid = start + (end - start) / 2; (避免出现start + end越界情况);
3). A[mid] 依次比较 ==, <, >;
4). A[start], A[end]与target比较。
二 常见问题
2.1 基础二分问题变种
1)Search a 2D matrix i
http://www.lintcode.com/zh-cn/problem/search-a-2d-matrix/
思路:将二维矩阵看做线性序列,则为典型二分搜索问题,所以只需要线性序列与二维数组小标的对应
即: mid / n , mid % n
public class Solution {
/*
* @param matrix: matrix, a list of lists of integers
* @param target: An integer
* @return: a boolean, indicate whether matrix contains target
*/
public boolean searchMatrix(int[][] matrix, int target) {
// write your code here
if(matrix == null || matrix.length == 0){
return false;
}
if(matrix[0] == null || matrix[0].length == 0){
return false;
}
int row = matrix.length;
int column = matrix[0].length;
int start = 0;
int end = row * column - 1;
while(start + 1 < end){
int mid = start + (end - start)/2;
int number = matrix[mid/column][mid%column];
if(number == target){
return true;
}else if(number < target){
start = mid;
}else{
end = mid;
}
}
if(matrix[start/column][start%column] == target|| matrix[end/column][end%column] == target){
return true;
}
return false;
}
}
2)Search a 2D matrix ii
http://www.lintcode.com/zh-cn/problem/search-a-2d-matrix-ii/
思路:根据矩阵的性质,从左下角向右上寻找。
如果target大于当前元素,当前列上方元素可删除,所以向右走一步;
如果target小于当前元素,当前行右方元素可删除,所以向上走一步。
public class Solution {
/*
* @param matrix: A list of lists of integers
* @param target: An integer you want to search in matrix
* @return: An integer indicate the total occurrence of target in the given matrix
*/
public int searchMatrix(int[][] matrix, int target) {
// write your code here
if(matrix == null || matrix.length == 0){
return 0;
}
if(matrix[0] == null || matrix[0].length == 0){
return 0;
}
int row = matrix.length;
int column = matrix[0].length;
int x = row - 1;
int y = 0;
int count = 0;
while(x >= 0 && y < column){
if(matrix[x][y] < target){
y++;
}else if(matrix[x][y] > target){
x--;
}else{
x--;
y++;
count++;
}
}
return count;
}
}
2.2 first/last position 问题
1)First Position of Target
http://www.lintcode.com/en/problem/first-position-of-target/
思路:找到第一个目标值出现的位置
public class Solution {
/**
* @param nums: The integer array.
* @param target: Target to find.
* @return: The first position of target. Position starts from 0.
*/
public int binarySearch(int[] nums, int target) {
// write your code here
if(nums == null || nums.length == 0){
return -1;
}
int left = 0;
int right = nums.length - 1;
while(left + 1 < right){
int mid = left + (right - left)/2;
if(nums[left] == target){
right = mid;
}else if(nums[mid] < target){
left = mid;
}else{
right = mid;
}
}
if(nums[left] == target){
return left;
}
if(nums[right] == target){
return right;
}
return -1;
}
}
2) Search insert position
http://www.lintcode.com/en/problem/search-insert-position/
思路:在数组中找到第一个大于等于target的位置(find first position)
public class Solution {
/**
* @param A: an integer sorted array
* @param target: an integer to be inserted
* @return: An integer
*/
public int searchInsert(int[] A, int target) {
// write your code here
if(A == null || A.length == 0){
return 0;
}
int start = 0;
int end = A.length - 1;
while(start + 1 < end){
int mid = start + (end -start)/2;
if(A[mid] == target){
return mid;
}else if(A[mid] < target){
start = mid;
}else{
end = mid;
}
}
if(A[start] >= target){
return start;
}
else if(A[end] >= target){
return end;
}
else if(A[end] < target){
return end+1;
}
return -1;
}
}
3) Find bad version
http://www.lintcode.com/en/problem/first-bad-version/
思路:找到第一个bad version(first position)
/**
* public class SVNRepo {
* public static boolean isBadVersion(int k);
* }
* you can use SVNRepo.isBadVersion(k) to judge whether
* the kth code version is bad or not.
*/
public class Solution {
/*
* @param n: An integer
* @return: An integer which is the first bad version.
*/
public int findFirstBadVersion(int n) {
// write your code here
int left = 1;
int right = n;
while(left + 1 < right){
int mid = left + (right - left)/2;
if(SVNRepo.isBadVersion(mid)){
right = mid;
}else{
left = mid;
}
}
if(SVNRepo.isBadVersion(left)){
return left;
}
if(SVNRepo.isBadVersion(right)){
return right;
}
return -1;
}
}
4) Sqrt(x)
http://www.lintcode.com/en/problem/sqrtx/
思路:找到最后一个平方小于等于x的数
public class Solution {
/**
* @param x: An integer
* @return: The sqrt of x
*/
public int sqrt(int x) {
// write your code here
if(x < 0){
return -1;
}
long start = 0;
long end = x;
while(start + 1 < end){
long mid = start + (end - start)/2;
if(mid * mid == x){
return (int)mid;
}else if(mid * mid < x){
start = mid;
}else{
end = mid;
}
}
if(end * end == x){
return (int)end;
}
return (int)start;
}
}
5)Search for a range(total-occurrence-of-target)
http://www.lintcode.com/en/problem/search-for-a-range/
思路:使用两遍模板,分别找到first 和 last position,则找到对应区间
public class Solution {
/**
* @param A: an integer sorted array
* @param target: an integer to be inserted
* @return: a list of length 2, [index1, index2]
*/
public int[] searchRange(int[] A, int target) {
// write your code here
if(A == null || A.length == 0){
return new int[]{-1, -1};
}
int[] bound = new int[2];
//寻找左边界,即第一次出现target值的位置
int start = 0;
int end = A.length - 1;
while(start + 1 < end){
int mid = start + (end - start)/2;
if(A[mid] == target){
end = mid;
}else if(A[mid] < target){
start = mid;
}else{
end = mid;
}
}
if(A[start] == target){
bound[0] = start;
}
else if(A[end] == target){
bound[0] = end;
}else{
bound[0] = -1;
bound[1] = -1;
}
//寻找右边界,即最后一次出现target值的位置
start = 0;
end = A.length - 1;
while(start + 1 < end){
int mid = start + (end - start)/2;
if(A[mid] == target){
start = mid;
}else if(A[mid] < target){
start = mid;
}else{
end = mid;
}
}
if(A[end] == target){
bound[1] = end;
}
else if(A[start] == target){
bound[1] = start;
}else{
bound[0] = -1;
bound[1] = -1;
}
return bound;
}
}
2.3 较复杂二分搜索问题(包含旋转排序数组内搜索)
这几个问题仍然采用二分思想,但二分后删除部分集合的调节并不明显,往往需要通过画图帮助理解。
同时此类问题很多也可以转换成增加条件的first position / last position问题,从而使用二分模板。
1) Find Minimum in Rotated Sorted Array
http://www.lintcode.com/en/problem/find-minimum-in-rotated-sorted-array/
思路: 根据旋转排序数组性质,问题转化为find first element which is bigger than nums[end];
public class Solution {
/**
* @param nums: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] nums) {
// write your code here
if(nums == null || nums.length == -1){
return -1;
}
int start = 0;
int end = nums.length - 1;
while(start + 1 < end){
int mid = start + (end - start)/2;
if(nums[mid] <= nums[end]){
//此时中间数在旋转点的右分支
end = mid;
}else{
start = mid;
}
}
return nums[start] > nums[end]?nums[end]:nums[start];
}
}
2) Search in Rotated Sorted Array
http://www.lintcode.com/en/problem/search-in-rotated-sorted-array/
思路:分情况进行“二分”的删除区间操作
首先区分target与nums[end]的关系
其次在不同区间内,根据target与nums[mid]的关系进行相关删除区间操作。
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) {
// write your code here
if(A == null || A.length == 0){
return -1;
}
int start = 0;
int end = A.length - 1;
while(start + 1 < end){
int mid = start + (end - start)/2;
if(A[mid] == target){
return mid;
}
if(A[mid] > A[end]){
//此时start和mid在同一个递增数组上
//接下来可以在有序数组上利用二分查找
if(A[start] <= target && target <= A[mid]){
end = mid;
}else{
start = mid;
}
}else{
//此时mid和end在同一个递增数组上
//接下来可以在有序数组上利用二分查找
if(A[mid] <= target && target <= A[end]){
start = mid;
}else{
end = mid;
}
}
}
if(A[start] == target){
return start;
}
if(A[end] == target){
return end;
}
return -1;
}
}
3) Find peak element
http://www.lintcode.com/en/problem/find-peak-element/
思路:因为题目假设了相邻位置的元素不相同,并且A[0]A[length-1].我们可以通过判断A[mid],A[mid-1],A[mid+1]的关系来去除左右半边
public class Solution {
/*
* @param A: An integers array.
* @return: return any of peek positions.
*/
public int findPeak(int[] A) {
// write your code here
if(A == null || A.length == 0){
return -1;
}
int start = 1;
int end = A.length - 2;
while(start+1 < end){
int mid = start + (end - start)/2;
if(A[mid-1] < A[mid] && A[mid+1] < A[mid]){
return mid;
}
else if(A[mid] < A[mid-1]){
//如果左边的节点比mid大,那么我们可以继续在左半区间查找,
//因为左边可以证明一定存在一个peak element
end = mid;
}else if(A[mid+1] > A[mid]){
start = mid;
}
}
if(A[start] < A[end]){
return end;
}else{
return start;
}
}
}