KEY:
如果面设题是要求在排序的数组(或者部分排序的数组)中查找一个数字或者统计某个数字出现的次数,那么我们都可以尝试使用二分法
转自力扣:
方法一:二分法
只要是一次可以排除一半,都可以用二分法。那么如何使用二分法呢?我们很自然会想到使用边界的值和中间位置的值进行比较。
注意:这里的提法是“中间数”,即位于中间的那个数,不是中位数,请注意区分。
可以分为以下两种情况:
1、中间数与左边界比较
尝试在纸上写出几个例子:
例 1:[1, 2, 3, 4, 5]
例 2:[2, 3, 4, 5, 1]
以上这两个例子,中间数都比左边界大,但是旋转排序数组的最小值可能在中间数的左边(例 1),也可能在中间数的右边(例 2),因此不能使用中间数与左边界比较作为二分法的讨论依据。
接下来,看看另一种讨论依据。
2、中间数与右边界比较:
(1)当中间数比右边界表示的数大的时候,中间数就一定不是目标数(旋转排序数组的最小值)。
还是尝试举个例子:
例 3:[7, 8, 9, 10, 11, 12, 1, 2, 3]
中间数 11 比右边界 3 大,因此中间数以及中间数前面的数都不是目标数,把左边界设置中间数位置 + 1,即 left = mid + 1;
(2)当中间数比右边界表示的数小的时候,中间数就可能是目标数(旋转排序数组的最小值),举个例子:
例 4:[7, 8, 1, 2, 3]
中间数 1 比右边界表示的数小的时候,说明,中间数到右边界是递增的(对于这道题是非递减),那么中间数右边的(不包括中间数)就一定不是目标数,可以把它们排除,不过中间数有可能是目标数,就如本例,因此,把右边界设置为 right = mid。
(3)当中间数与右边界表示的数相等的时候,看下面两个例子:
例 5:[0, 1, 1, 1, 1, 1, 1]
例 6:[1, 1, 1, 1, 0, 1, 1]
目标值可能在中间数的左边,也可能在中间数的右边,那么该怎么办呢?很简单,此时你看到的是右边界,就把只右边界排除掉就好了。正是因为右边界和中间数相等,你去掉了右边界,中间数还在,就让中间数在后面的循环中被发现吧。
因此,根据中间数和右边界的大小关系,可以使用二分法搜索到目标值。
作者:liweiwei1419
链接:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/solution/er-fen-fa-fen-zhi-fa-python-dai-ma-by-liweiwei1419/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
思考: 如果中间数大于最右面的数 说明最小的数在中间数和最右面数之间,因为数组是排完序的 最左边的元素肯定大于最右边的数
package Arrays;
public class s6 {
public static void main(String[] args) {
int[] arr = new int[]{1,1,1,1,0,1};
System.out.println(s6.minNumberInRotateArray(arr));
}
public static int minNumberInRotateArray(int[] array) {
if (array == null || array.length == 0) return 0;
int left = 0;
int right = array.length - 1;
while (left < right) {
int mid = (left + right) / 2;
if (array[mid] > array[right]) {
left = mid + 1;
} else if (array[mid] == array[right]) {
right--;
} else if (array[mid] < array[right]){
right = mid;
}
}
return array[left];
}
}