二分法题型:
(常规的三种主流写法:(<=)对应mid+1,mid-1; (<)对应mid+1,mid; (+1<)对应mid,mid;)
(无证明推测:一般找具体tar的在循环内部返回的才会用<=(指的是返回全部情况 而不是个别情况);因为用<=循环后lo!=hi了,而是lo=hi+1;
所以不是内部返回的一般都用(lo<hi),对应的下面指针变化必须要有一个是+1/-1的,不能两个都是mid;
mid偏左,则lo=mid+1;mid偏右,则hi=mid-1;)
1.Classical Binary Search:http://www.lintcode.com/en/problem/classical-binary-search/
经典二分查找:
解法:(循环条件<=; 指针变化lo=mid+1 /hi=mid-1;)(PS: mid=lo+((hi - lo)>>1 防止溢出+移位替代除法提升速度)
public class Solution { public int findPosition(int[] nums, int target) { int lo = 0; int hi = nums.length - 1; while (lo <= hi) { int mid = lo + ((hi - lo) >> 1); if (nums[mid] == target) return mid; else if (nums[mid] < target) lo = mid + 1; else hi = mid - 1; } return -1; } }
public class Solution { public int findPosition(int[] nums, int target) { int lo = 0; int hi = nums.length - 1; while (lo < hi) { int mid = lo + ((hi - lo) >> 1); if (nums[mid] == target) return mid; else if (nums[mid] > target) hi = mid; else lo = mid + 1; } return -1; } }
2.First Position of Target:http://www.lintcode.com/en/problem/first-position-of-target/
查找第一个目标数:
解法1:(在while循环内,使用一个rst记录=tar的mid值;while(lo<=hi),如果>=tar,则hi=mid-1,否则lo=mid+1)
(具体过程分析:与常规的二分查找相同,不过是当=tar的时候,1.rst记录值,防止此时的mid已经是最左了;2.hi=mid-1继续查找左边的数;)
class Solution { public int binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int lo = 0, hi = nums.length - 1; int rst = -1; while (lo <= hi) { int mid = lo + ((hi - lo) >> 1); if (nums[mid] >= target) { hi = mid - 1; } else { lo = mid + 1; } if (nums[mid] == target) rst = mid; } return rst; } }
解法2:(while(lo<hi); 如果=tar,则hi=mid,大于则mid-1,小于则mid+1;最后循环外判断lo=tar?lo;)
(注意:一定是lo<hi而不能是<=;不然错误!例如[1,4],1; 1-lo/mid, 4-hi; 1-lo/mid/hi;死循环!即)
class Solution { public int binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int lo = 0, hi = nums.length - 1; while (lo < hi) { int mid = lo + ((hi - lo) >> 1); if (nums[mid] == target) { hi = mid; } else if (nums[mid] < target) { lo = mid + 1; } else { hi = mid - 1; } } if (nums[lo] == target) { return lo; } return -1; } }
(或者合并=和>) (使用这种解法)
(循环条件(lo<hi); mid=lo+((hi-lo)>>1); 判断if(>=)hi=mid; 否则lo=mid+1; 循环外判断[lo]=tar;)
class Solution { public int binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int lo = 0, hi = nums.length - 1; while (lo < hi) { int mid = lo + ((hi - lo) >> 1); if (nums[mid] < target) { lo = mid + 1; } else { hi = mid; } } if (nums[lo] == target) { return lo; } return -1; } }
解法3:(九章模板;(+1<) + mid + 分别判断lo和hi;)
class Solution { 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; // or start = mid + 1 } else { end = mid; // or end = mid - 1 } } if (nums[start] == target) { return start; } if (nums[end] == target) { return end; } return -1; } }
3.Find Last Position of Target:http://www.lintcode.com/zh-cn/problem/last-position-of-target/
查找最后一个目标数:
解法1:(与First解法1相同)
class Solution { public int binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int lo = 0, hi = nums.length - 1; int rst = -1; while (lo <= hi) { int mid = lo + ((hi - lo) >> 1); if (nums[mid] <= target) { lo = mid + 1; } else { hi = mid - 1; } if (nums[mid] == target) rst = mid; } return rst; } }
解法2:
(循环条件(lo<hi); mid=lo+((hi-lo+1)>>1); 判断if(<=)lo=mid; 否则hi=mid-1; 循环外判断[hi]=tar;)
class Solution { public int binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int lo = 0, hi = nums.length - 1; while (lo < hi) { int mid = lo + ((hi - lo + 1) >> 1); if (nums[mid] <= target) { lo = mid ; } else { hi = mid - 1; } } if (nums[hi] == target) { return hi; } return -1; } }
4.Search for a Range:http://www.lintcode.com/en/problem/search-for-a-range/
搜索区间(找出第一个和最后一个tar的位置):
解法1:(即整合First和Last的解法即可;)
1 public class Solution { 2 public int[] searchRange(int[] A, int target) { 3 int[] rst = new int[]{-1,-1}; 4 if (A.length == 0) { 5 return rst; 6 } 7 int lo = 0, hi = A.length - 1; 8 while (lo <= hi) { 9 int mid = lo + (hi - lo) / 2; 10 if (A[mid] >= target) { 11 hi = mid - 1; 12 } else { 13 lo = mid + 1; 14 } 15 if (A[mid] == target) rst[0] = mid; 16 } 17 if (rst[0] == -1) { 18 return rst; 19 } 20 lo = rst[0]; 21 hi = A.length - 1; 22 while (lo <= hi) { 23 int mid = lo + (hi - lo) / 2; 24 if (A[mid] <= target) { 25 lo = mid + 1; 26 } else { 27 hi = mid - 1; 28 } 29 if (A[mid] == target) rst[1] = mid; 30 } 31 return rst; 32 } 33 }
解法2:(//First: lo+(hi-lo)/2; while(<); (>=)mid; mid+1; if(lo) //Last : lo+(hi-lo+1)/2; while(<=); (<=)mid; mid-1; if(hi);)
(注意:第二个一定要是lo+(hi-lo+1)/2;不然mid偏左则会死循环;例如[5,5],5;)
public class Solution { public int[] searchRange(int[] A, int target) { int[] rst = new int[]{-1,-1}; if (A.length == 0) { return rst; } int lo = 0, hi = A.length - 1; while (lo < hi) { int mid = lo + (hi - lo) / 2; if (A[mid] >= target) { hi = mid; } else { lo = mid + 1; } } if (A[lo] == target) rst[0] = lo; else return rst; hi = A.length - 1; while (lo < hi) { int mid = lo + (hi - lo + 1)/ 2; if (A[mid] <= target) { lo = mid; } else { hi = mid - 1; } } if (A[hi] == target) rst[1] = hi; return rst; } }
解法3:(九章模板)
public class Solution { public int[] searchRange(int[] A, int target) { if (A.length == 0) { return new int[]{-1, -1}; } int start, end, mid; int[] bound = new int[2]; // search for left bound start = 0; end = A.length - 1; while (start + 1 < end) { 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] = bound[1] = -1; return bound; } // search for right bound start = 0; end = A.length - 1; while (start + 1 < end) { 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] = bound[1] = -1; return bound; } return bound; } }
5.Find Minimum in Rotated Sorted Array:http://www.lintcode.com/en/problem/find-minimum-in-rotated-sorted-array/
寻找旋转排序数组中的最小值:
解法1:(while(lo<hi); 首先判断([lo]>[hi])return[lo]; 然后再判断[mid]和[start]; >=则lo=mid+1; 否则hi=mid; return[lo];)
(其实思想是找出第一个小于[lo]的值;比较[mid]和[lo];)
([5,6,7,1,2,3,4] 如果[lo]<[hi] 那么说明没有旋转; 比较mid和start,如果>=,说明旋转的在后半部分(不含mid)例如[2,1]/[2,3,1],而[2,3,4]是已经判断过了的所以不会出现;如果<,说明在前半部分(包含mid),例如[3,1,2]包含mid)
public class Solution { public int findMin(int[] nums) { int lo = 0, hi = nums.length - 1; while (lo < hi) { if (nums[lo] < nums[hi]) return nums[lo]; int mid = lo + (hi - lo) / 2; if (nums[mid] >= nums[lo]) lo = mid + 1; else hi = mid; } return nums[lo]; } }
解法2:(思想:找出第一个小于[hi]的值;如果mid<=hi, 那么hi=mid;mid>hi, 那么mid那么lo=mid; 与Find First的套路一致! 其实和解法1一样……解法1更优化.
不过这里的[hi]是变化的,而九章的解法里是把hi固定为最后一位)
public class Solution { public int findMin(int[] nums) { int lo = 0, hi = nums.length - 1; while (lo < hi) { int mid = lo + (hi - lo) / 2; if (nums[mid] > nums[hi]) lo = mid + 1; else hi = mid; } return nums[lo]; } }
(注意:如果使用解法1,则必须判断[lo]<[hi]情况;而解法2则不需要;)
6.Search a 2D Matrix : http://www.lintcode.com/en/problem/search-a-2d-matrix/
搜索二维矩阵:
解法1:(两次二分搜索;第一次循环必须是while(+1<),对应循环内部是mid,mid;最后分别判断[end]和[start]与tar; 第二次循环,普通的二分法;)
public class Solution { /** * @param 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 start = 0; int end = matrix.length - 1; int row ; while (start + 1 < end) { int mid = start + (end - start)/2; if (matrix[mid][0] == target){ return true; } else if (matrix[mid][0] < target) { start = mid ; } else { end = mid ; } } if (matrix[end][0] <= target){ row = end; } else if (matrix[start][0] <= target){ row = start; } else { return false; } start = 0; end = matrix[0].length - 1; while (start <= end) { int mid = start + (end - start)/2; if (matrix[row][mid] == target){ return true; } else if (matrix[row][mid] < target) { start = mid + 1; } else { end = mid - 1; } } return false; } }
解法2:(一次二分搜索;看作是将Matrix看成m*n长的list,mid对应的number=Matrix[mid/col][mid%col])
public class Solution { /** * @param 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 start = 0; int row = matrix.length; int col = matrix[0].length; int end = row * col - 1; while (start <= end){ int mid = start + (end - start) / 2; int num = matrix[mid / col][mid % col]; if (num == target){ return true; } else if (num < target){ start = mid + 1; } else { end = mid - 1; } } return false; } }
LeetCode:
1.Search in Rotated Sorted Array:https://leetcode.com/problems/search-in-rotated-sorted-array/#/description
找出旋转排序数组的tar值:
(findMin+常规Binary Search)
解法:(先找出最小值位置index:(lo<hi)三步-判断([lo]<[hi]),判断([lo]<=mid),else;index是最小值位置,也是数组旋转的位数; 再用常规二分搜索- realmid=(mid+index)%n;判断realMid和tar,指针移动为mid+1/mid-1;)
public class Solution { public int search(int[] nums, int target) { int lo = 0, n = nums.length; int hi = n - 1; int index = findMinIndex(nums); while (lo <= hi) { int mid = lo + (hi - lo) / 2; int realMid = (mid + index) % n; if (nums[realMid] == target) return realMid; else if (nums[realMid] < target) lo = mid + 1; else hi = mid - 1; } return -1; } public int findMinIndex(int[] nums) { int lo = 0, hi = nums.length - 1; while (lo < hi) { if (nums[lo] < nums[hi]) return lo; int mid = lo + (hi - lo) / 2; if (nums[lo] <= nums[mid]) lo = mid + 1; else hi = mid; } return lo; } }
2.Find Minimum in Rotated Sorted Array II:https://leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/#/description
找出旋转排序数组最小值II(含重复):
解法1:(基于Minimum的解法1;区别就是mid=lo的时候,lo++)
public class Solution { public int findMin(int[] nums) { int lo = 0, hi = nums.length - 1; while (lo < hi) { if (nums[lo] < nums[hi]) return nums[lo]; int mid = lo + (hi - lo) / 2; if (nums[mid] > nums[lo]) lo = mid + 1; else if (nums[mid] < nums[lo]) hi = mid; else lo++; } return nums[lo]; } }
解法2:(与MinimumI解法2相同,区别就是mid=hi的时候,hi--,这样此时不会移除最小值;因为mid=hi有两种情况,一是mid-hi之间全部相等,二是mid前的所有数都等于hi;其实和解法1一样……)
1 public class Solution { 2 public int findMin(int[] nums) { 3 int lo = 0, hi = nums.length - 1; 4 while (lo < hi) { 5 int mid = lo + (hi - lo) / 2; 6 if (nums[mid] > nums[hi]) lo = mid + 1; 7 else if (nums[mid] < nums[hi]) hi = mid; 8 else hi--; 9 } 10 return nums[lo]; 11 } 12 }
3.Search Insert Position:https://leetcode.com/problems/search-insert-position/#/description
寻找插入位置(找到tar则返回改位置,否则返回插入位置):
解法:(和常规Binary Search相同;唯一区别是BS找不到返回-1,该题找不到返回lo! [2,4]示例,lo/mid-2,hi-4;若tar-3 则mid<tar, lo=mid+1; 此时lo刚好为目标位置;若tar-1 则mid>tar,hi=mid-1,为lo的前一个位置,但是插入的位置是要替换lo的位置的,所以目标位置仍然为lo; )
public class Solution { public int searchInsert(int[] nums, int target) { int lo = 0, hi = nums.length - 1; while (lo <= hi) { int mid = lo + (hi - lo) / 2; if (nums[mid] == target) { return mid; } else if (nums[mid] > target) { hi = mid - 1; } else { lo = mid + 1; } } return lo; } }