一、题目
1、寻找旋转排序数组中的最小值(LC 153)
2、寻找两个正序数字的中位数(LC 4)
二、题解
1、寻找旋转排序数组中的最小值(LC 153)

(1)分析
这道题的数组是经过旋转的有序数组,整体不再完全有序,但仍然保持了两段各自有序的特性,所以不能直接遍历,可以使用二分查找。利用二分不断缩小范围,最终定位到最小值的位置。
采用闭区间二分写法,先定义好左右两个指针,然后在循环中不断计算中间位置,核心判断逻辑是把中间元素和数组最后一个元素做比较。如果中间元素小于或等于末尾元素,就说明最小值一定出现在左半区间,这时候我就把右指针移到中间位置左边,继续向左寻找。如果中间元素大于末尾元素,就说明最小值在右半区间,于是把左指针移到中间位置右边,向右缩小范围。
只依靠中间元素和末尾元素的大小关系,就能一步步缩小搜索范围。当循环结束时,左指针指向的位置就是整个数组的最小值所在下标,直接返回对应元素即可。
(2)解答
java
class Solution {
public int findMin(int[] nums) {
int n = nums.length;
int left = 0;
int right = n - 1;
//二分查找 , 闭区间
while(left <= right){
int mid = (left + right)/2;
if(nums[n - 1] >= nums[mid]){ //中间元素与末尾元素比较
right = mid - 1; //末尾元素大或者相等,去左边找
}else{
left = mid + 1; //末尾元素小,去右边找
}
}
return nums[left];
}
}
2、寻找两个正序数字的中位数(LC 4)

(1)分析
这道题是二分查找里难度较高的一道题,题目最关键的要求就是时间复杂度必须控制在 O (log (m+n)) 以内,所以不能把两个数组合并后再查找,只能使用二分分割的思想来解决。需要抓住中位数的本质,就是把两个有序数组分成左右两部分,让左边的所有元素都小于右边的所有元素,再根据总长度的奇偶性计算结果。
可以先保证 nums1 是长度更短的那个数组,这样可以减少二分的次数。新建了两个数组,在原数组的最前面和最后面分别加上最小值和最大值,这样就不用在分割时特意判断数组越界的问题,写起来轻松很多。
接下来就是核心的分割,用两个变量分别表示两个数组的分割位置,保证左边所有元素的总个数接近总长度的一半。当满足左边最大元素小于右边最小元素时,就说明找到了正确的分割方式。如果两个数组总长度是奇数,中位数就是左边部分的最大值;如果是偶数,就取左边最大值和右边最小值的平均值。通过不断调整分割位置来满足条件,最终直接算出中位数。
(2)解答
java
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
//保证nums1是长度较短的数组
if(nums1.length > nums2.length){
int[] temp = nums1;
nums1 = nums2;
nums2 = temp;
}
int m = nums1.length;
int n = nums2.length;
int[] a = new int[m+2];
int[] b = new int[n+2];
a[0] = Integer.MIN_VALUE;
b[0] = Integer.MIN_VALUE;
a[m+1] = Integer.MAX_VALUE;
b[n+1] = Integer.MAX_VALUE;
//复制数组,避免繁琐的边界条件判断
//nums1拷贝到a,nums2拷贝到b,同时在首尾分别加两个元素
System.arraycopy(nums1, 0, a, 1, m);
System.arraycopy(nums2, 0, b, 1, n);
int i = 0; //a[i] 分割后数组a左边最大的元素
int j = (m+n+1)/2; //b[j] 分割后数组b左边最大的元素
//i+j=(m+1+1)/2
while(true){
if(a[i] <= b[j + 1] && b[j] <= a[i+1]){ //符合条件的分割
int max1 = Math.max(a[i], b[j]); //左侧的最大值
int min2 = Math.min(a[i+1], b[j+1]); //右侧的最小值
return (m+n)%2 > 0 ? max1 : (max1 + min2)/2.0;
}
i++;
j--;
}
}
}