二分查找的前提条件是数组是完全有序或者部分有序,其时间复杂度通常为O(logn).
二分查找模板(可应对多种不同需求)
public class BinarySearchTemplate { public int binarySearch(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int begin = 0, end = nums.length - 1; while (begin + 1 < end) { int mid = begin + (end - begin)/2; if (nums[mid] == target) { return mid; // can be modified } if (nums[mid] < target) { begin = mid; }else { end = mid; } } if (nums[begin] == target) { return begin; // can be modified } if (nums[end] == target) { return end; // can be modified } return -1; } }
可使用上述模板解决的问题
Problem 1: Search Insert Position
Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You may assume no duplicates in the array.
Here are few examples.[1,3,5,6]
, 5 → 2[1,3,5,6]
, 2 → 1[1,3,5,6]
, 7 → 4[1,3,5,6]
, 0 → 0
(leetcode: https://leetcode.com/problems/search-insert-position/)
Solution
public class Solution { public int searchInsert(int[] nums, int target) { if (nums == null || nums.length == 0) { return 0; } int begin = 0, end = nums.length - 1; while (begin + 1 < end) { int mid = begin + (end - begin) / 2; if (nums[mid] < target) { begin = mid; } else { end = mid; } } if (target <= nums[begin]) { return begin; } if (target > nums[end]) { return end + 1; } return end; } }
Problem 2: Search for a Range
Given a sorted array of integers, find the starting and ending position of a given target value.
Your algorithm's runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1]
.
For example,
Given [5, 7, 7, 8, 8, 10]
and target value 8,
return [3, 4]
.
Solution
public class Solution { public int[] searchRange(int[] nums, int target) { int[] res = {-1, -1}; if (nums == null || nums.length == 0) { return res; } res[0] = getLowerIdx(nums, target); if (res[0] == -1) { return res; } res[1] = getHigherIdx(nums, target); return res; } public int getLowerIdx(int[] nums, int target) { int begin = 0, end = nums.length - 1; while (begin + 1 < end) { int mid = begin + (end - begin) / 2; if (nums[mid] < target) { begin = mid; } else { end = mid; } } if (nums[begin] == target) { return begin; } if (nums[end] == target) { return end; } return -1; } public int getHigherIdx(int[] nums, int target) { int begin = 0, end = nums.length - 1; while (begin + 1 < end) { int mid = begin + (end - begin) / 2; if (nums[mid] <= target) { begin = mid; } else { end = mid; } } if (nums[end] == target) { return end; } if (nums[begin] == target) { return begin; } return -1; } }
Problem 3: Search in Rotated Sorted Array
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
Solution
public class Solution { public int search(int[] nums, int target) { if (nums == null || nums.length == 0) { return -1; } int begin = 0, end = nums.length - 1; while (begin + 1 < end) { int mid = begin + (end - begin) / 2; if (nums[mid] == target) { return mid; } if (nums[mid] > target) { if (target < nums[begin] && nums[mid] > nums[begin]) { begin = mid; } else { end = mid; } } else { // note: boundary if (target >= nums[begin] && nums[mid] < nums[begin]) { end = mid; } else { begin = mid; } } } if (nums[begin] == target) { return begin; } if (nums[end] == target) { return end; } return -1; } }
容易错误的Case:
[5, 1, 3] , 5;
[1, 3, 5], 1;
Problem 4: Square Root
用最快的方法求一个数的平方根。
public class SquareRoot { public double getSquareRoot(double d) { if (d < 0) { return -1; } if (d == 0 || d == 1) { return d; } double low = 0.0; double high = d < 1.0 ? 1 : d; double epsilo = 1e-6; double mid = 0; while (true) { mid = low + (high - low) / 2.0; if (Math.abs(mid * mid - d) < epsilo) { break; } if (mid * mid < d) { low = mid; } else { high = mid; } } return mid; } }
Problem 5: Find Minimum in Rotated Array
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).
Find the minimum element.
You may assume no duplicate exists in the array.
public class Solution { public int findMin(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int low = 0, high = nums.length - 1; if (nums[high] > nums[low]) { return nums[low]; } while (low + 1 < high) { int mid = low + (high - low) / 2; if (nums[mid] < nums[mid - 1] && nums[mid] < nums[mid + 1]) { return nums[mid]; } if (nums[mid] > nums[low]) { low = mid; } else { high = mid; } } return nums[low] < nums[high] ? nums[low] : nums[high]; } }
Follow up
Follow up for "Find Minimum in Rotated Sorted Array":
What if duplicates are allowed?Would this affect the run-time complexity? How and why?
Suppose a sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).
Find the minimum element.
The array may contain duplicates.
public class Solution { public int findMin(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int len = nums.length; // increasing... if (nums[0] < nums[len - 1]) { return nums[0]; } int begin = 0, end = len - 1; while (begin + 1 < end) { int mid = begin + (end - begin) / 2; if (nums[mid] < nums[mid - 1] && nums[mid] < nums[mid + 1]) { return nums[mid]; } if (nums[mid] > nums[begin]) { begin = mid; }else if (nums[mid] < nums[begin]) { end = mid; } else if (nums[mid] > nums[end]) { begin = mid; } else { // nums[begin], nums[end]和nums[mid]均相等,则顺序遍历。 return traversal(nums); } } if (nums[begin] < nums[end]) { return nums[begin]; } return nums[end]; } private int traversal(int[] nums) { int min = Integer.MAX_VALUE; for (int n : nums) { min = Math.min(min, n); } return min; } }
Sorted Array
Problem 1: Remove Duplicates from Sorted Array I
Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length.
Do not allocate extra space for another array, you must do this in place with constant memory.
For example,
Given input array nums = [1,1,2]
,
Your function should return length = 2
, with the first two elements of nums being 1
and 2
respectively. It doesn't matter what you leave beyond the new length.
public class Solution { public int removeDuplicates(int[] nums) { if (nums == null || nums.length == 0) { return 0; } int len = nums.length; int p1 = 0, p2 = 1; while (p2 < len) { if (nums[p1] != nums[p2]) { ++p1; ++p2; continue; } while (p2 < len && nums[p2] == nums[p1]) { ++p2; // find the first elem not equal to nums[p1] } if (p2 == len) { break; } nums[p1 + 1] = nums[p2]; ++p1; } return p1 + 1; } }
Problem 2: Remove Duplicates from Sorted Array II
Follow up for "Remove Duplicates":
What if duplicates are allowed at most twice?
For example,
Given sorted array nums = [1,1,1,2,2,3]
,
Your function should return length = 5
, with the first five elements of nums being 1
, 1
, 2
, 2
and 3
. It doesn't matter what you leave beyond the new length.
解决思路
前指针的起始位置为第二个元素,后指针需要根据指向元素和前指针以及前指针的前一个元素的比较进行移动。
public class Solution { public int removeDuplicates(int[] nums) { if (nums == null) { return 0; } if (nums.length < 3) { return nums.length; } int len = nums.length; int p1 = 1, p2 = 2; while (p2 < len) { if (nums[p1 - 1] == nums[p1] && nums[p1] == nums[p2]) { while (p2 < len && nums[p2] == nums[p1]) { ++p2; } if (p2 == len) { break; } } // swap int tmp = nums[p1 + 1]; nums[p1 + 1] = nums[p2]; nums[p2] = tmp; ++p1; ++p2; } return p1 + 1; } }
Problem 3:Merge Sorted Array
Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array.
Note:
You may assume that nums1 has enough space (size that is greater or equal to m + n) to hold additional elements from nums2. The number of elements initialized in nums1and nums2 are m and n respectively.
解决思路
双指针 + 从后往前插入。
程序
public class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int i = m - 1; int j = n - 1; int k = nums1.length - 1; while (i >= 0 && j >= 0) { if (nums1[i] > nums2[j]) { nums1[k--] = nums1[i--]; } else { nums1[k--] = nums2[j--]; } } while (i >= 0) { nums1[k--] = nums1[i--]; } while (j >= 0) { nums1[k--] = nums2[j--]; } } }
Problem 4: Median of Two Sorted Array
There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).
解决思路
递归,二分;注意边界条件;
程序
public class Solution { public double findMedianSortedArrays(int[] nums1, int[] nums2) { int len = nums1.length + nums2.length; if (len % 2 == 0) { // 偶数时,返回中间两个数的均值 return (findKthElem(nums1, 0, nums2, 0, len / 2) + findKthElem(nums1, 0, nums2, 0, len / 2 + 1)) / 2.0; } return findKthElem(nums1, 0, nums2, 0, len / 2 + 1); } private double findKthElem(int[] a, int a_start, int[] b, int b_start, int k) { if (a_start >= a.length) { return b[b_start + k - 1]; } if (b_start >= b.length) { return a[a_start + k - 1]; } if (k == 1) { // 返回a和b数组中首元素较小的那个 return Math.min(a[a_start], b[b_start]); } int ak = a_start + k / 2 - 1 < a.length ? a[a_start + k / 2 - 1] : Integer.MAX_VALUE; int bk = b_start + k / 2 - 1 < b.length ? b[b_start + k / 2 - 1] : Integer.MAX_VALUE; if (ak < bk) { return findKthElem(a, a_start + k / 2, b, b_start, k - k / 2); } else { return findKthElem(a, a_start, b, b_start + k / 2, k - k / 2); } } }
总结
1. 数组有序或者部分有序:考虑二分查找;
2. 数组无序:转成有序,考虑二分。