209-Minimum Size Subarray Sum
给定含有n个正整数的数组,和一个正整数target,找出该数组中满足其和>=target的长度最小的连续子数组,并返回其长度,如果不存在,返回0
//方法有误,暂未查出
public int minSubArrayLen03(int target,int[] nums) {
int result=0;
for(int i=0;i<nums.length;i++) {
result+=nums[i];
}
if(result<target) {
return 0;
}
int i=0,j=nums.length-1;
while(i<j) {
if(nums[i]<nums[j]) {
if(result-nums[i]>=target) {
result=result-nums[i];
i++;
}else {
return j-i+1;
}
}else if(nums[i]>nums[j]){
if(result-nums[j]>=target) {
result=result-nums[j];
j--;
}else {
return j-i+1;
}
}else if(nums[i]==nums[j]) {
if(result-nums[i]>=target) {
if((i+1)<(j-1)) {
if((nums[i+1]<nums[j-1])) {
result-=nums[i];
i++;
} else {
result-=nums[j];
j++;
}
}else {
result-=nums[i];
i++;
}
}else {
return j-i+1;
}
}
}
return j-i+1;
}
1,暴力法
初始化子数组长度为数组长度,枚举数组中的每个下标作为子数组的开始下标,//对于每个开始下标i,需要找到大于等于i的最小下标j,使得nums[i]到nums[j]的元素和大于等于s
更新数组的最小长度
public int minSubArrayLen04(int s,int[] nums) {
int n=nums.length;
//特殊情况
if(n==0) {
return 0;
}
int ans=Integer.MAX_VALUE;
for(int i=0;i<n;i++) {
int sum=0;
for(int j=i;j<n;j++) {
sum+=nums[j];
if(sum>=s) {
ans=Math.min(ans, j-i+1);
break;
}
}
}
return ans==Integer.MAX_VALUE?0:ans;//o(n2),o(1)
}
2,前缀和+二分查找
为了使用二分查找,需要额外创建一个数组sums用于存储数组nums的前缀和,
其中sums[i]表示从nums[0]到nums[i-1]的元素和。
对每个下标i,通过二分查找得到大于或等于i的最小下标bound,使得sums[bound]-sums[i-1]>=s
更新子数组的最小长度,子数组的长度是bound-(i-1)
public int minSubArrayLen05(int s,int[] nums) {
int n=nums.length;
if(n==0) {
return 0;
}
int ans=Integer.MAX_VALUE;
int[] sums=new int[n+1];
//sums[0]=0表示前0个元素的前缀和为0
//数组递增,因为为正
for(int i=1;i<=n;i++) {
sums[i]=sums[i-1]+nums[i-1];
}
for(int i=1;i<=n;i++) {
int target=s+sums[i-1];//也就是target-sums[i-1]=s
int bound =Arrays.binarySearch(sums, target);
//在sums中搜索值为target的元素,返回索引。
// 搜索值不是数组元素,且大于数组内元素,索引值为 – (length + 1);
//搜索值不是数组元素,且小于数组内元素,索引值为 – 1。
if (bound < 0) {
bound = -bound - 1;
//length:找不到最后结果会小于0
//0 :找不到的话最后结果会小于0
}
if (bound <= n) {
ans = Math.min(ans, bound - (i - 1));
}
}
return ans == Integer.MAX_VALUE ? 0 : ans;
}
//二分查找的时间复杂度是logn,遍历的时间复杂度是n,所以总时间复杂度是O(nlog(n))
3,滑动窗口
定义两个指针start和end分别表示子数组的开始位置和结束位置,
维护sum存储子数组中的元素和,即从nums[start]到nums[end]的元素和
初始,start和end都指向0,sum值为0,
每一轮迭代将nums[end]加到sum,如果sum>=s,则更新子数组最小长度end-start+1
然后将nums[start]从sums中减去并将start右移,直到sum<s,
在此过程中同样更新子数组的最小长度。每一轮迭代最后end右移。
public int minSubArrayLen06(int s,int[] nums) {
int n=nums.length;
if(n==0) {
return 0;
}
int ans=Integer.MAX_VALUE;
int start=0,end=0;
int sum=0;
while(end<n) {//不够大就一直加
sum+=nums[end];
while(sum>=s){
ans=Math.min(ans, end-start+1);
sum-=nums[start];
start++;
}
end++;
}
return ans==Integer.MAX_VALUE?0:ans;
}
704-Binary Search (未找到返回-1)
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
//二分查找,不用仔细看。
public int search(int[] nums,int target) {
int n=nums.length;
if((nums[0]>target)||(nums[n-1])<target) {
return -1;
}
int i=0;
for(i=0;i<n;i++) {
if(nums[i]==target) {
return i;
}
}
return -1;//循环出来也没找到,说明没有
}
//二分查找
//初始化指针left=0,right=n-1 当left<=right
//循环,当left<right
//比较中间元素nums[privot=(left+right)/2]和目标值target
//如果target=nums[pivot],返回pivot
//如果target<nums[pivot],则在右侧继续搜索right=pivot-1
//如果target>nums[pivot],则在右侧继续搜索left=pivot+1
public int search01(int[] nums,int target) {
int n=nums.length;
int left=0,right=n-1,pivot=0;
while(left<=right) {
//<=因为可能出现只有一个元素的情况
pivot=left+(right-left)/2;
//返回中间 位置的下标,判断并进行下一次二分,可以防止right+left可能会溢出
if(target==nums[pivot]) {
return pivot;
}else if(target>nums[pivot]) {//向右侧搜索
left=pivot+1;
}else {
right=pivot-1;
}
}
//出了循环还没找到,说明没有
return -1;
}
35-Search Insert Position (未找到元素时返回待插入元素的位置,若存在多个目标元素,则返回任意一个) (二分查找避免死循环注意项)
//给定排序数组和目标值,在数组中找到目标值,并返回其索引,如果目标值不存于数组中,返回它将会按顺序插入的位置
public int searchInsert(int[] nums, int target) {
int n=nums.length;
//排除,target不在数组范围内
if(target<=nums[0])
return 0;
//比最后一个数大放在n位置
if(target>nums[n-1])
return n;
//和最后一个数相等放在n-1位置
if(target==nums[n-1])
return n-1;
int i=0;
while(i<n) {
if(nums[i]>=target) {
return i;
}else{
i++;
}
}
return i;
}
//暴力解决需要O(n)的时间复杂度,二分的话则可以降低到O(logn)
//思路和二分查找没有区别,设定左侧下标left和右侧下标right中间下标mid
//每次根据nums[mid]和target之间的大小进行判断,相等则直接返回下标
//查找结束没有相等值返回left
模板
public int searchInsert01(int[] nums,int target) {
int left=0,right=nums.length-1;
while(left<=right) {
//这里=因为可能数组只有一个元素
int mid=left+(right-left)/2;//5/2=2
if(nums[mid]==target) {
}else if(nums[mid]<target) {
left=mid+1;//去右边找,从mid+1到right,剔除mid点
}else if(nums[mid]>target){
right=mid-1;//左边找,从left到mid-1,剔除了mid点
}
}
return 0;
}
34-Find First and Last Position of Element in Sorted Array (返回目标元素的左右边界,不存在时则返回{-1,-1}) (34升级)
//34排序数组
//在排序数组中查找该元素在数组中第一个和最后一个的位置
//如果没有返回[-1,1]
//由于数组已经排序,整个数组是单调递增的,使用二分法来加速查找过程
//考虑target的开始位置和结束位置,我们要找的就是数组中,
//第一个等于target的位置和第一个大于target的位置
public int[] searchRange(int[] nums,int target) {
int leftIdx=binarySearch(nums,target,true);
int rightIdx=binarySearch(nums,target,false)-1;
if(leftIdx<=rightIdx&&rightIdx<nums.length&&nums[leftIdx]==target&&nums[rightIdx]==target) {
return new int[] {leftIdx,rightIdx};
}
return new int[] {-1,-1};
}
public int binarySearch(int[] nums, int target, boolean lower) {
int left=0,right=nums.length-1,ans=nums.length;
while(left<right) {
int mid=(left+right)/2;
if(nums[mid]>target||(lower&&nums[mid]>=target)) {
//找第一个等于或者最大的小于它的,找第一个大于它
right=mid-1;
ans=mid;
}else {
left=mid+1;
}
}
return 0;
}
//while(left<=right)在退出循环的时候left=right+1,即right在左,left在右
//while(left< right)在退出循环的时候,left==right
//1,找第一次出现的位置,二分查找,找到了继续向左找 2,查找出现的最后一个位置,向右
public int[] searchRange01(int[] nums,int target) {
if(nums.length==0) {
return new int[] {-1,-1};
}
int firstPosition=findFirstPosition(nums,target);
int lastPosition=findLastPosition(nums,target);
return new int[] {firstPosition,lastPosition};
}
private int findFirstPosition(int[] nums,int target) {
int left=0;
int right=nums.length-1;
while(left<=right) {
int mid=left+(right-left)/2;
if(nums[mid]==target) {
//不可以直接返回,应该继续向左边找,即[left,mid-1]区间里找
right=mid-1;
}else if(nums[mid]<target) {
//应该继续向右边找,即[mid+1,right]区间里找
left=mid+1;
}else {//此时 nums[mid]>target,应该继续向左找,即[left,mid-1]区间找
right=mid-1;
}
}
//此时left和right位置left=right+1,此时left才是第一次元素出现的位置
//因此还需要特别做一次判断
if(left!=nums.length&&nums[left]==target) {
return left;
}
return -1;
}
private int findLastPosition(int[] nums,int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
// 只有这里不一样:不可以直接返回,应该继续向右边找,即 [mid + 1, right] 区间里找
left = mid + 1;
} else if (nums[mid] < target) {
// 应该继续向右边找,即 [mid + 1, right] 区间里找
left = mid + 1;
} else {
// 此时 nums[mid] > target,应该继续向左边找,即 [left, mid - 1] 区间里找
right = mid - 1;
}
}
if (right != -1 && nums[right] == target) {
return right;
}
return -1;
}
33-Search in Rotated Sorted Array (有序数组旋转,二分查找的线性思维)
//整数数组nums按升序排列,数组中的值互不相同
//在传递给函数之前,nums在预知未知的某个下标k上进行了旋转,(0=<k<nums.length)
//使数组变为nums[k],nums[k+1]...nums[n-1],nums[n],nums[0],nums[1]...nums[k-1]
//[0,1,2,4,5,6,7]在下标3处经过旋转后可能变为[4,5,6,7,0,1,2]
//给旋转后的数组nums和一个整数target,
//如果nums中存在这个目标值target,则返回它的索引,否则返回-1
//无序数组排序
//1,将旋转数组转换为有序数组进行查询,
//找到最值将旋转数组分成 两段有序数组,接下来在有序数组中找到目标值
//找到最小的索引,将数组分成升序的两端,
//根据nums[0]和target的关系判断在左段还是右段
//对于旋转数组nums=[4,5,6,7,0,1,2]
//先根据nums[0]与target的关系判断target在左段还是右段
//例如target=5,比nums[0]大,目标值在左半段,在[4,5,6,7,inf,inf,inf]里找
//例如target=1,比nums[0]小,目标值在右半段,在[-inf,-inf,-inf,-inf,0,1,2]里找
public int search(int[] nums,int target) {
int left=0,right=nums.length-1;
while(left<=right) {
int mid=left+right-left)/2;
if(nums[mid]==target) {
return mid;
}
//先根据nums[0]与target的关系判断目标值是在左半段还是右半段
if(target>=nums[0]) {
//目标值在左半段时,若mid在右半段,则将mid的值改成inf
if(nums[mid]<nums[0]) {
nums[mid]=Integer.MAX_VALUE;
}
}else {
//target<=nums[0]目标值在右半段时,若mid在左半段,则将mid索引的值改成-inf
if(nums[mid]>=nums[0]) {
nums[mid]=Integer.MIN_VALUE;
}
}
if(nums[mid]<target) {
left=mid+1;
}else {
right=mid-1;
}
}
return -1;
}
//先根据nums[mid]与nums[lo]的关系判断mid是在左段还是右段,
//接下来再判断target是在mid的左边还是右边,从而来调整左右边界lo和hi
public int search(int[] nums,int target) {
int left=0,right=nums.length-1,mid=0;
while(left<right) {
mid=left+(right-left)/2;
if(nums[mid]==target) {
return mid;
}
//先根据nums[mid]与nums[left]的关系判断mid是在左段还是右段
if(nums[mid]>=nums[left]) {
//再判断target是在mid的左边还是右边,从而调整左右边界left和right
if(target>=nums[left]&&target<nums[mid]) {//left<target<mid在左半段
right=mid-1;
}else {//left<mid,但是target不在他们中间,所以target在右半段
left=mid+1;
}
}else {//left>mid
if(target>nums[mid]&&target<=nums[right]) {
//mid<target<right,在右半段
left=mid+1;
}else {//left>mid,但是target不在mid和right中间
right=mid-1;
}
}
}
return -1;
}
81-Search in Rotated Sorted Array II (33题目的升级)
//假设按照升序排序的数组在预知未知的某个点上进行了旋转
//编写一个函数来判断给定的目标值是否存在于数组中,存在返回true,否则返回false
//超出时间限制
public boolean search04(int[] nums,int target) {
if(nums==null||nums.length==0) {
return false;
}
int left=0,right=nums.length-1,mid=0;
while(left<=right) {
mid=left+(right-left)/2;
if(target==nums[mid]) {
return true;
}
if(nums[left]==nums[mid]) {
left++;
continue;
}
//判断nums[0]和nums[mid]的大小
if(nums[left]<nums[mid]) {
if(nums[left]<=target&&target<nums[mid]) {
right=mid-1;
//如果有,在左端,改的是right,left没有改,所以应该包含左边界
}else {
left=mid+1;
}
}else{
if((target>nums[mid])&&(target<=nums[right])) {
//在右半段,改left,所以包含右端相等情况
left=mid+1;
//这里mid位置与target不等,只包含nums[right]相等的情况,所以可以直接去掉
}else {
right=mid-1;
}
}
}
//循环到最后还是没有
return false;
}
153-Find Minimum in Rotated Sorted Array (二分查找线性思维的理解)
//寻找旋转排序数组中的最小值
//这种最大值最小值一般是相邻的
//左中右三个位置的值相比较,有以下几种情况:
//1,左值<中值<右值,最小值在最左边
//2,左值>中值<右值,最小值在左半边
//3,左值<中值>右值,最小值在右半边
//4,左值>中值>右值,两个单调递减,不可能
public int findMin(int[] nums) {
int left=0;
int right=nums.length-1;
while(left<right) {
//要保证左闭右开区间里始终套住最小值,所以不包含=
int mid=left+(right-left)/2;
if(nums[mid]>nums[right]) {//中>右,在右半边
left=mid+1;
}else if(nums[mid]<nums[right]) {
right=mid;
}
//整数除法是向下取整,mid更靠近left
//结合while条件,可以知道left<=mid,mid<right,mid始终小于right
//因此在while循环内,nums[mid]要么大于要么小于nums[right],不会等于
//这样else {right=mid;}可以改为else if(nums[mid]<nums[right]){right=mid;}
//while循环退出的条件:如果输入数组只有一个数,左右边界位置重合,left==right,不会进入while循环,直接输出。
//如果输入数组多于一个数,循环到最后只会剩下两个数,nums[left]==nums[mid],以及nums[right],这里的位置left==mid==right-1
//如果num[left]==nums[mid]>nums[right],则左边大,右边小,需要执行left=mid+1,
//使得left==right,左右边界位置重合,循环结束,nums[left]与nums[right]都保存了最小值
//如果nums[left]==nums[mid]<nums[right],则左边小右边大,会执行right=mid,
//使得left=right,左右边界位置重合,循环结束,left,mid,right都保存了最小值
//
}
return nums[left];//最小值一定存在且无法比较,所以只能圈住它
}
//1,左值<中值<右值,最大值在最右边
//2,左值>中值<右值,最大值在右半边
//3,左值<中值>右值,最大值在中值这里
//最大值的下一位就是最小值,while(left<right)把最大值圈出来
public int findMin02(int[] nums) {
int left=0,right=nums.length-1,mid=0;
while(left<right) {
mid=left+(right-left+1)/2;//先加一再除,mid更靠近右边的right
if(nums[mid]<nums[right]) {
left=mid;//向右移动左边界
}else {
right=mid-1;//向左移动右边界
}
//这里right和left的变化依然考虑只有两个数和一个数的情况
//两个数的话,left+1=mid=right,这时left=mid和right=mid-1都可以回到最终right在最大值位置
}
return nums[(right+1)%nums.length];
//最大值向右移动一位就是最小值了,考虑最大值在最右边的情况
}
154-Find Minimum in Rotated Sorted Array II (153题的升级)
//寻找旋转排序数组中的最小值2
//寻找旋转数组中的最小元素,数组中可能出现重复的元素
//旋转数组包含 重复元素,可以采用right=right-1来解决这个问题,此操作不会丢失最小值
//若nums[right]是唯一最小值,不可能满足判别条件nums[mid]==nums[right]
//若nums[right]不是唯一最小值,由于mid<right而nums[mid]==nums[right]
//最小值依然在left和right-1中间
//旋转过的数组是两个小升序数组
/*
* 1,mid>right时,最小值在mid和right中间left=mid+1,mid不可能是最小值可以直接跳过
* 2,mid<right时,最小值在左边,mid可能时最小值,所以不跳过right=mid
* 3,mid=right时,right=right-1
* while(left<right),用循环包围住最小值,然后mid=left+(right-left)/2靠近left
* 或者mid=left+(right-left+1)/2来靠近right
* */
//结果错误(已改正)
public int findMin03(int[] nums) {
int left=0,right=nums.length-1,mid=0;
while(left<right) {
mid=left+(right-left)/2;
if(nums[mid]>nums[right]) {
left=mid+1;
}else if(nums[mid]<nums[right]) {
right=mid;
}else {
right=right-1;
}
}
return nums[left];
//关于这个right和left怎么变化 ,在数多的时候就是排除,所以不影响
//所以要选取特殊例子,如3,1和1,3.
//3,1 l=0,r=1,m=0,最后要得出1,left=mid+1
//1,3 l=0,r=1,m=0,最后要得出0,right=mid
}
public int finMin04(int[] nums) {
int left=0,right=nums.length-1;
while(left<right) {
int mid=(left+right)/2;
if(nums[mid]>nums[right]) left=mid+1;
else if(nums[mid]<nums[right]) right=mid;
else right=right-1;
}
return nums[left];
}
162-Find Peak Element
//寻找峰值,峰值元素是指大于左右相邻值的元素,给一个数组,找到峰值返回索引
//数组可能包含多个峰值,返回任意一个即可,只用大于左右两个数即可
//从i=1开始寻找,这个想法是错误的
public int findPeakElement(int[] nums) {
//注意特殊情况,端点只需要比一边大就可以
//只有一个元素,本身就是峰值
int n=nums.length-1;
if(nums.length<2) return 0;
if(nums[0]>nums[1]) return 0;
if(nums[n]>nums[n-1]) return n;
for(int i=1;i<nums.length-1;i++) {
if(nums[i]>nums[i-1]&&nums[i]>nums[i+1]) {
return i;
}
}
return -1;
}
852-Peak Index in a Mountain Array
//符合下列属性的数组称为山脉数组
//arr.length>=3,0<i<arr.length-1
//存在i使得,arr[0]<arr[1]<…arr[i]>…>arr[length-1]
//最大值在最左边和最右边的不是山脉数组
//最大值周围离他越远数值越小
//遍历数组,如果后面的值大于前面的i++,当开始变小就返回i o(n)
//二分法,求最大值,数组是两个数组合在一起,一个单调递增在左/,一个单调递减在右\
//左<中,最大值在中到右中间,left=mid,这里有问题,左<中很可能是/\这种
//左>=中,最大值在左到中中间,right=mid-1
//左<中<右,中右之间是最大值
//左<=中>右,left--,right--
//左>中>=右,左中之间
//左>中<右,不可能出现
//特殊情况,只有三个元素,132,left=1,mid=3,right=2
//2131,left=2,mid=1,right=1,
//13100,left=1,mid=1,right=0
public int peakIndexInMountainArray(int[] arr) {
int left=0,right=arr.length-1,mid=0;
while(left<right) {
mid=left+(right-left)/2;
if(arr[left]<arr[mid]&&arr[mid]<arr[right])
left=mid;
else if(arr[left]>arr[mid]&&arr[mid]>=arr[right])
right=mid-1;
else if(arr[mid]>=arr[left]&&arr[mid]>arr[right])
{left--;right--;}
}
return left;
}
//官方方法
public int peakIndexInMoutainArray(int[] A) {
int left=0,right=A.length-1;
while(left<right) {
int mid=left+(right-left)/2;
if(A[mid]<A[mid+1])//单调递增的话这里就是处于后半段
left=mid+1;
else//不单调递增这里就是前半段
right=mid;
}
return left;
}
69-Sqrt(x)
//x的平方根,实现int sqrt(int x)函数
// 输入: 4
// 输出: 2
// 示例 2:
//
// 输入: 8
// 输出: 2
// 说明: 8 的平方根是 2.82842…,
// 由于返回类型是整数,小数部分将被舍去。
//二分法搜索平方根的思想,高了降低,低了就涨。
//一个数的平方根肯定不会超过它的一半
//一个数的一半的平方大于它自己,那么这个数>=4,或者<=0
//判断数在哪两个平方中间,选左边那个
//1,4,9,16,25,36,49,64,81,100,121,144,169,
//0,2,4,8,12,
//0,1,2,4,6,
//从它的1/4遍历到1/2
public int mySqrt(int x) {
if(x==0) {
return 0;
}
if(x<4) {
return 1;
}
int i=0;
for(i=x/4;i<x/2+1;i++) {
if(i*i>x) {
return i;
}
}
return i;
}
//官方解答
public int mySqrt01(int x) {
//注意:针对特殊测试用例,2145839,所以把搜索的范围设置成长整型
//为了照顾到0,把左边界设置成0
long left=0;
//为了照顾到1把有边界设置为x/2+1
long right=x/2+1;
while(left<right) {
//这里一定去右中位数,如果取左中位数,代码会进入死循环
//long mid=left+(right-left+1)/2;
long mid=(left+right+1)>>>1;//除2
//10 << 1 = 20,10 << 3 = 80,6 >> 1 = 3,6 >> 2 = 1
long square=mid*mid;
//右中位数,当只有两位数时left=0,mid=1,right=1 left=1或者right=0
if(square>x) {
right=mid-1;
}else {
left=mid;
//如果取左中位数,就会一直取mid,在只有两个数的时候,如果取左中位数,就不会变
}
}
return (int)left;
}
//牛顿法
public int mySqrt03(int a) {
long x=a;
while(x*x>a) {
x=(x+a/x)/2;
}
return (int)x;
}
74-Search a 2D Matrix
//编写一个高效的算法来判断m*n矩阵中,是否存在一个目标值。该矩阵具有如下特性
//每行中的整数从左到右按升序排列,每行的第一个整数大于前一行的最后一个整数
//可以用二分法,把矩阵看成数组
//m*n的矩阵可以视为长度为m*n的有序数组
public boolean searchMatrix(int[][] matrix,int target) {
int left=0,right=matrix.length*matrix[0].length-1;
while(left<=right) {
int mid=(left+right)/2;
int midVal=matrix[mid/matrix[0].length][mid%matrix[0].length];
if(midVal==target)return true;
else if(midVal>target) right=mid-1;
else left=mid+1;
}
return false;
}
240-Search a 2D Matrix II (74题的升级)
//编写一个高效的算法来搜索m*n矩阵中的一个目标值
//每行元素从左到右升序排列,每列元素从上到下升序排列
//矩阵已经排过序,需要使用二分法搜索以加快我们的算法
//这个算法的时间复杂度是O(log(n!)),主循环中执行的工作量最大,运行min(m,n)次迭代
//每次迭代中,我们对长度为m-i和n-i的数组片执行两次二分搜索,因此循环的每一次迭代中
private boolean binarySearch0(int[][] matrix,int target,int start,boolean vertical) {
int lo=start;
int hi=vertical?matrix[0].length-1:matrix.length-1;
while(hi>=lo) {
int mid=(lo+hi)/2;
if(vertical) {//搜索一列column
if(matrix[start][mid]<target) {
lo=mid+1;
}else if(matrix[start][mid]>target) {
hi=mid-1;
}else {
return true;
}
}else {//搜索一行row
if(matrix[mid][start]<target) {
lo=mid+1;
}else if(matrix[mid][start]>target) {
hi=mid-1;
}else {
return true;
}
}
}
return false;
}
public boolean searchMatrix01(int[][] matrix,int target) {
//首先确保矩阵不为空,迭代矩阵对角线,从当前元素对列和行搜索,
//保持从当前对开始的行和列为己排序。
//我们总是可以二分搜索这些行和列切片
if(matrix==null||matrix.length==0)
return false;
int shorterDim=Math.min(matrix.length,matrix[0].length);
for(int i=0;i<shorterDim;i++) {
boolean verticalFound =binarySearch0(matrix,target,i,true);
boolean horizontalFound=binarySearch0(matrix,target,i,false);
if(verticalFound||horizontalFound) {
return true;
}
}
return false;
}
//对于已排序的数组,有两种方法可以确定一个任意元素目标是否可以用常数时间判断,第一
//如果数组的区域为0,则它不包含元素,如果target小于数组最小值,大于数组最大值,那么矩阵不包含目标值
//如果目标值包含在数组内,因此我们沿着索引行的矩阵中间列,如果我们找到target,立即返回true
//matrix[row-1][mid]<target<matrix[row][mid]
//矩阵可以围绕这个索引分为四个子矩阵,左上和右下矩阵不能包含目标,所以我们可以从搜索空间中删除它们
private int[][] matrix;
private int target;
private boolean searchRec(int left,int up,int right,int down) {
//矩阵没有高度和宽度
if(left>right||up>down) {
return false;
//target比最大值大比最小值小
}else if(target<matrix[up][left]||target>matrix[down][right]) {
return false;
}
int mid=left+(right-left)/2;
//定位row,通过matrix[row-1][mid]<target<matrix[row][mid],把矩阵划分成4个
int row=up;
while(row<=down&&matrix[row][mid]<=target) {
if(matrix[row][mid]==target) {
return true;
}
row++;
}
//继续搜索两个小矩阵
return searchRec(left,row,mid-1,down)||searchRec(mid+1,up,right,row-1);
}
public boolean searchMatrix02(int[][] mat,int targ) {
matrix=mat;
target=targ;
if(matrix==null||matrix.length==0) {
return false;
}
return searchRec(0,0,matrix[0].length-1,matrix.length-1);
}
//方法4,初始化指向矩阵左下角的指针,指向值>target,向上;指向值<target,向右;
//矩阵的特性,左上是最小值,右下是最大值
public boolean searchMatrix03(int[][] matrix,int target) {
int row=matrix.length-1;//行
int col=0;//列
while(row>=0&&col<matrix[0].length) {
if(matrix[row][col]>target) {
row--;
}else if(matrix[row][col]<target) {
col++;
}else {
return true;
}
}
return false;
}
875-Koko Eating Bananas
//这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉,H小时内最多拿多少
//每个小时选1堆,多于K则拿K,少于K全部拿
//返回他H小时内吃掉所有的最小速度
//H-nums.length=超出次数
//算法错误
public int minEatingSpeed(int[] piles,int h) {
Arrays.sort(piles);
if(piles==null)
return 0;
if(h==piles.length) {//长度为h
return piles[h-1];
}
if(h==piles.length+1) {//长度为h-1
return piles[h-3];//第二大
}
long sum=0;//数很可能很大
for(int i=0;i<piles.length;i++)
sum+=piles[i];
return (int) (sum/h+1);
}
//如果珂珂能够以K的进食速度最终吃完所有的香蕉,H小时内,possible(K)=true
//那么存在X,当K>=X时,possible(K)=true
//当初始条件为3,6,7,11时,存在X=4使得possible(1,2,3)=false,possible(4,5,)=true
//我们二分查找possible(K)的值,来找到第一个使得possible(x)为true的X,
//我们将每一堆的完成时间加在一起和H比较
public int minEatingSpeed01(int[] piles,int H) {
int lo=1;
int hi=1_000_000_000;
//结果值范围可以定义一下,最慢速度,除去第一堆外,其他每堆都是0根,
//最大速度除去第一堆,其他每堆都是一根
while(lo<hi) {
int mi=(lo+hi)/2;
if(!possible(piles,H,mi))
lo=mi+1;
else
hi=mi;
}
return lo;
}
public boolean possible(int[] piles,int H,int K) {
int time=0;
for(int p:piles)
time+=(p-1)/K+1;//!!!!!!!
return time<=H;
}
1011-Capacity To Ship Packages Within D Days
//1011在D天内送达包裹的能力
//传送带上的i个包裹重量为weight[i],按给出的重量顺序往传送带装载包裹
//装载重量不会超过最大载重G
//返回D天内能将传送带上的所有包裹送达的船的最低运载能力
//最小是,刚好每天都运了最大载重,和/D.
//最大是,((和+1)/D-1)*2, G/2,G/2+1,G/2+1 1,G//shipwithdays方法有误
public static int shipWithinDays(int[] weights,int D) {
int len=weights.length;
int sum=0,max=0;
if(len==0)
{return 0;}
for(int num:weights) {
max=Math.max(max, num);
sum+=num;
}
/*要在D天内运完所有包裹,那么每天至少的承重量为sum/D
*但是,因为一次至少运一个包裹!!,而包裹可大可小,那么可能weight[i]>sum/D
*所以最低承重max(sum/D,maxWeight)*/
//最低承载量
int left=Math.max(sum/D, max);//向下取整
//最高承载量
int right=((sum+1)/D-1) *2+1;//向上取整,防止失去边界i
while(left<right) {
//int mid=left+(right-left)>>>1;//向左贴近
int mid=(right+left)/2;
if(isok(weights,mid,D))
right=mid;
else
left=mid+1;
}
return left;
}
//判断G最大载重,能否在D天内装完
private static boolean isok(int[] weights, int G,int D) {
int temp=0;
for(int val:weights) {
if(temp+val>G) {
temp=0;
D--;
}
temp+=val;
}
return D>0;
}
public static boolean shipwithdays(int [] weight,int G,int D) {
int time=0;
int i=0;int sum=0;
while(i<weight.length) {
if(sum<=G) {
sum+=weight[i];
if(i<weight.length) {
i++;//装上了,指向下一个
}
}else {
i--;
sum-=weight[i];
System.out.println("sum="+sum);
time++;System.out.println(time+"车");
sum=0;//再开始新的一车
//装不下拿出来,送走
System.out.println("i="+i);
}
}
System.out.println("跳出循环");
System.out.println(time);
if(time>D)
return false;
return true;
}
373-Find K Pairs with Smallest Sums
//给定两个升序排列的整形数组nums1和nums2,以及一个整数k
//定义一对值(u,v),其中一个元素来自nums1,第二个元素来自nums2
//找到和最小的k对数字(u1,v1)(u2,v2)…(uk,vk),元素可以重复使用
//利用一个数组来保存nums1中每个元素对应的最小组合的nums2下标,
//每次只需要最多遍历n次就能获取剩下可用的最小组合,获取之后,对应的下标就加一,
//防止重复获取,双指针,指的就是f数组的下标对应的nums1的下标元素,
//f数组中的元素值对应的就是nums2下标元素,
public List<List<Integer>> kSmallestPairs01(int[] nums1,int[] nums2,int k){
List<List<Integer>> res=new ArrayList<>();
int n=nums1.length,m=nums2.length;
if(n==0||m==0)return res;
//利用一个数组来保存nums1中每个元素对应的最小组合的nums2下标,初始值0,因为刚开始对应的是最小元素
int[] f=new int[n];
//外层最多遍历k次,获取前k个最小值
while(res.size()<k) {
int cur=0;
//遍历每个nums1元素对应nums2最小可用组合,并获取最小组合
for(int i=1;i<n;i++) {
//当前i位置可用组合已经用完了
if(f[i]==m)continue;
//比较获取最小的组合
if(f[cur]==m||nums1[cur]+ nums2[f[cur]]>nums1[i]+nums2[f[i]]) {
cur=i;
}
}
//所有的组合都用完了,就跳出循环
if(f[cur]==m)break;
//答案中添加当前组合
res.add(Arrays.asList(nums1[cur],nums2[f[cur]]));
//当前组合中nums1元素对应的nums2元素下标加一,这样就不会重复用到之前的组合
f[cur]++;
}
return null;
}
378-Kth Smallest Element in a Sorted Matrix (二分查找解空间
的理解)
//有序矩阵中第k小的元素
//给定n*n矩阵,其中每行每列元素按升序排序,找到矩阵中第k小的元素
//它是排序后的第k小元素,而不是第k个不同的元素
//由题目给出的性质可知,这个矩阵的每一行均为一个有序数组,
//问题转化为n个有序数组中找第k大的数,可以想到归并排序的做法,归并到第k个数即可停止
//本题是n个数组归并,需要用小根堆维护
public int kthSmallest02(int[][] matrix,int k) {
PriorityQueue<int[]> pq=new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[]a,int[]b) {
return a[0]-b[0];//小根堆比较器
}
});
int n=matrix.length;//n行
for(int i=0;i<n;i++) {
pq.offer(new int[] {matrix[i][0],i,0});//把左端最小值加入最小堆
}
for(int i=0;i<k-1;i++) {
int[] now=pq.poll();
//取出栈顶元素,也就是最小值,以及最小值所在的位置,取出k-1次,下次取出的就是第k小的值
if(now[2]!=n-1) {
//如果不是这一行最后一位的端点值,就把这一行的下一位放入最小堆中
//每次弹出候选人中的最小值,然后把上次弹出的候选人的右边一个补进来,就能保证全局最小值在候选人列表中产生
pq.offer(new int[] {matrix[now[1]][now[2]+1],now[1],now[2]+1});
}
}
return pq.poll()[0];
}
//方法三,二分查找
//由题目给出的性质,这个矩阵的元素是从左下到右下递增的(假设矩阵左上角为matrix[0][0])
//整个数组中matrix[0][0]为最小值,matrix[n-1][n-1]为最大值,
//
//初始位置在matrix[n-1][0]左下角,设当前位置为matrix[i][j],若matrix[i][j]<=mid,则将当前所在列的不大于mid的数的数量
//即i+1累加到答案中,并向右移动,否则向上移动
//不断移动直到走出格子为止
public int kthSmallest03(int[][] matrix,int k) {
int n=matrix.length;
int left=matrix[0][0];//最小值
int right=matrix[n-1][n-1];//最大值
while(left<right) {
int mid=left+((right-left)>>1);
if(check(matrix,mid,k,n)) {
right=mid;
}else {
left=mid+1;
}
}
return left;
}
public boolean check(int[][] matrix,int mid,int k,int n) {
int i=n-1;
int j=0;
int num=0;
while(i>=0&&j<n) {
if(matrix[i][j]<=mid) {
num+=i+1;//比mid小的多加i+1个,看下一列
j++;
}else {
i--;//向下移动变小,看这一列有多少比mid小的
}
}
return num>=k;//比mid小的数多于k个,mid减小
}
668-Kth Smallest Number in Multiplication Table
//668乘法表中第k小的数
//给定高度m,宽度n的一张m*n乘法表,需要返回表中第k小的数字
/输入: m = 3, n = 3, k = 5
输出: 3
解释:
乘法表:
1 2 3
2 4 6
3 6 9/
//二分搜索,当且仅当乘法表中存在小于或等于k,enough(x)才为真。
//通俗的说,enough(x)描述了x是否足够大可以成为乘法表中第k小的值。
//然后,对于A,当x>=A,enough(x)为true,x<A,enough(x)为false
//二分搜索中,循环不变量enough(hi)=true,开始时,enough(mn)=true,并且每当设置hi时
//都将其设置为enough(hi)=true,开始时,enough(mn)=true,并且每当设置hi时,都将其设置为enough
//正确尝试
//需要在第i行寻找一个大于num的个数,只要min(num/i,n)
//其中(i是这一行的行号,n是矩阵的行列数)
//num/i代表如果num也在第i行它存在的列数,
//所以只要取最小值就是第i行不大于num的个数
/*1 2 3
2 4 6
3 6 9
/
//第2行不大于4的个数:min(不大于4/第二行2,矩阵的列数3)=2个(就是2,4)
//确定这个乘法表中不大于num的个数,就将每一行不大于num的个数累加即可
//初始化left=1,right=nm+1,mid=(left+right)/2,在m,n中寻找不超过mid的个数
public boolean enough(int x,int m,int n,int k) {
int count=0;
for(int i=1;i<=m;i++) {//m行
count+=Math.min(x/i, n);//第i行寻找不大于x的数
}
return count>=k;
}
public int findKthNumber01(int m,int n,int k) {
int lo=1,hi=m*n;
while(lo<hi) {
int mi=lo+(hi-lo)/2;
if(!enough(mi,m,m,k))lo=mi+1;
else hi=mi;//最后肯定是lo=mi=hi-1
}
return lo;
}
//先构造乘法表,然后再去搜索,这样不行,m,n的取值可能非常大,非常耗内存
//m,n的乘法表中取值范围为[1,mn],可以使用二分搜索
//失败的尝试
//向右向下增长的矩阵找第k小的数字,自己生成mn的矩阵
public int findKthNumber(int m,int n,int k) {
//最小堆
PriorityQueue<int[]> pq=new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] a, int[] b) {
// TODO 自动生成的方法存根
return a[0]-b[0];//小顶堆中存放数组,第一位放值,第二三位放位置
}
});
for(int i=0;i<m-1;i++) {
//pq.offer(new int[] {})
}
return k;
}
719-Find K-th Smallest Pair Distance (复杂二分查找问题的精髓)
//给定一个整数数组,返回所有数对之间的第k个最小距离。
//一对(A,B)的距离被定义为A和B之间的绝对差值
//由于第k小的距离一定在[0,W=max(nums)-min(nums)]中,我们在这个区间上进行二分。
//对于当前位置guess,统计距离小于等于guess的距离对数量,并根据与k的关系整理上下界
//定义函数possible(guess)为真,当且仅当距离小于等于guess的距离对数量比k大和k相等
//if possible true,right=mid,因为可能等于,所以边界不能去掉
//if possible false,left=mid+1,返回left,因为最后left=mid=right-1
//二分查找+双指针
//使用双指针来计算除所有小于等于guess的距离对数目.right通过循环逐渐递增,
//left在每次循环中被维护,使得它满足nums[right]-nums[left]<=guess且最小
//对于nums[right],以它为右端的满足距离小于等于guess的距离对数目即为right-left
//我们在循环中对这些right-left进行累加,就得到了所有小于等于guess的距离对数目
public int smallestDistancePair(int[] nums,int k) {
Arrays.sort(nums);
int lo=0;
int hi=nums[nums.length-1]-nums[0];
while(lo<hi) {
int mi=(lo+hi)/2;
int count=0,left=0;
for(int right=0;right<nums.length;++right) {
while(nums[right]-nums[left]>mi)left++;
count+=right-left;
}
//count=number of pairs with distance<=mi
if(count>=k)hi=mi;//有可能是=k所以要保留
else lo=mi+1;
}
return lo;
}
/*题目中条件里写的len(nums)<=10000,也就是说数据量为10的4次方,
*那么时间复杂度上O(n2)基本是超时的,O(n)基本不可能,所以想到O(nlogn),
*顺势也就能基本猜出用的是二分法,下面是二分法的具体实现
*/
//对数组进行排序,找出最大距离对,nums[len-1]-nums[0],第k小的距离对一定在最大和最小距离对之间
//有了范围我们可以使用二分查找,通过while循环将所以<=mid的距离对的个数算出来,加到count中
//如果count>=k,那么right=mid,如果count<k,那么left=mid+1;
public int smallestDistancePair01(int[] nums,int k) {
Arrays.sort(nums);
int len=nums.length;
int low=0;
int high=nums[len-1]-nums[0];
while(low<high) {
int mid=low+(high-low)/2;
int count=0;
int left=0;
for(int right=0;right<len;right++) {
while(nums[right]-nums[left]>mid)left++;
count+=right-left;
}
if(count>=k) {
high=mid;
}else {
low=mid+1;
}
}
return low;
}
786-K-th Smallest Prime Fraction (373,378,668,719类似n*n的矩阵每行和每列都排序的k-th问题的解题思路)
//786
/*给你一个按递增顺序排列的数组arr和一个整数k,数组arr和一个整数k,数组arr由1和若干素数组成,且其中所有整数互不相同
- 对于没对满足0<i<j<arr.length的i和j,可以得到分数arr[i]/arr[j].那么第k个最小的分数是多少,以长度为2的整数数组返回答案
- answer[0]==arr[i]且answer[1]==arr[j]*/
//分数的比较不能用int,应该用float,或者比分子分母
//二分法,范围是从arr[0]/arr[n-1]到1
//under(x)求解小于x的分数数量,这是一个关于x的单调增函数,因此可以通过二分查找求解
//使用二分查找找出一个x,使得小于x的分数恰好有K个,并且记录其中最大的一个分数
//under(x)函数有两个目的:返回小于x的分数数量以及小于x的最大分数,这是为了找到第k小的。
//在under(x)函数中使用滑动窗口的方法:对于每个primes[j],找出最大的i使得primes[i]/prime[j]<x
//随着j和primes[j]的增加,i也会随之增加。
public int[] kthSmallestPrimeFraction(int[] primes,int K) {
double lo=0,hi=1;
int[] ans=new int[] {0,1};
while(hi-lo>1e-9) {//1*10的-9次方
double mi=lo+(hi-lo)/2.0;
int[] res=under(mi,primes);
if(res[0]<K) {//res中第一位放小于mi的分数个数
lo=mi;
}else {
ans[0]=res[1];
ans[1]=res[2];
hi=mi;
}
}
return ans;
}
public int[] under(double x,int[] primes) {
int numer=0,denom=1,count=0,i=-1;
for(int j=1;j<primes.length;++j) {
while(primes[i+1]<primes[j]*x)++i;// i+1/j < x, j固定,i+1前面的也会小于x,j处共有i+1个
count+=i+1;
//放最大的那个分数i/j > numer/denom
if(i>=0&&numer*primes[j]<denom*primes[i]) {
numer=primes[i];
denom=primes[j];
}
}
return new int[] {count,numer,denom};
}
4-Median of Two Sorted Arrays (难题,理解即可)
//给定两个大小分别为m和n的正序(从小到大)数组nums1和nums2.找出并返回这两个正序数组的中位数
//两个数组的中位数,需要先合并这两个数组,然后根据奇数还是偶数,返回中位数
public double finMedianSortedArrays(int[] nums1,int[] nums2) {
int[] nums;
int m=nums1.length;
int n=nums2.length;
nums=new int[m+n];
if(m==0) {
if(n%2==0) {
return(nums2[n/2-1]+nums[n/2])/2.0;
}else {
return nums2[n/2];
}
}
if(n==0) {
if(m%2==0) {
return(nums2[m/2-1]+nums[m/2])/2.0;
}else {
return nums2[m/2];
}
}
int count=0;
int i=0,j=0;//i指向nums1,j指向nums2
//把没放完的继续放进去
while(count!=(m+n)) {
if(i==m) {
while(j!=n) {
nums[count++]=nums2[j++];
}
break;
}
if(j==n) {
while(i!=m) {
nums[count++]=nums1[i++];
}
break;
}
if(nums1[i]<nums2[j]) {
nums[count++]=nums[i++];
}else {
nums[count++]=nums[j++];
}
}
if(count%2==0) {
return (nums[count/2-1]+nums[count/2])/2.0;
}else {
return nums[count/2];//时间复杂度,空间复杂度嗾使m+n
}
}
//我们不需要将两个数组真的合并,只需要找到中位数在哪里就可以了
//思路是写一个循环,然后里边判断是否到了中位数的位置,如果到了就返回结果
//把奇数偶数的情况合并一下,用len表示合并后数组的长度,如果是奇数,我们需要知道第(len+1)/2
//如果是偶数,需要知道第len/2和len/2+1个数,也需要遍历len/2+1次。遍历的话,奇数偶数都是len/2+1次
//返回中位数,奇数返回最后一次遍历的结果就可以了,偶数需要最后一次和上一次遍历结果
//用两个变量left和right,right保存当前循环的结果,每次循环前将right的值赋给left,这样最后一次循环的时候,
//left将得到right的值,right更新为最后一次的结果
public double findMedianSortedArrays(int[] A,int[] B) {
int m=A.length;
int n=B.length;
int len=m+n;
int left=-1,right=-1;
int aStart=0,bStart=0;
for(int i=0;i<=len/2;i++) {//只用取前len/2+1位数就可以了,也就是从0,1,2...len/2
left=right;
if(aStart<m&&(bStart>=n||A[aStart]<B[bStart])) {
right=A[aStart++];
}else {
right=B[bStart++];
}
}
if((len&1)==0)//判断奇偶,偶数输出0,奇数输出1.
return (left+right)/2.0;
else
return right;
}//时间复杂度O(m+n),空间复杂度o(1)
//上面的方法都无法达到O(log(m+n)),很明显需要用二分法
//求中位数其实就是求第k小数的一种特殊情况,
//解法2中,我们一次遍历就相当于去掉一个不可能是中位数的一个值,也就是一个一个排除。
//由于数列是有序的,我们可以一半半的排除。假设要找第k小数,我们可以每次循环排除掉k/2个数。
//比较两个数组的第k/2个数字,如果k是奇数,向下取整。
//也就是比较第3个数字,上边数组中的4和下边数组中的3,如果哪个小,
//就表明该数组的前k/2个数字都不是第k小数字,把这些数去掉,新的数组继续进行比较
//去掉的数就一定是前面的,所以新的数组中找到第k-k/2小的数字就好了
//每次都是取k/2的数字比较,有时候回遇到数组长度小于k/2的时候,这时把箭头指向它的末尾
//由于上面的数组已经空了,只需要返回别的数组的数字就可以了
//从上边可以看到,无论第奇数个还是偶数个数字,对算法无影响,在算法进行中,k的值都有可能从奇数变偶数
//最终都会变为1,或者一个数组空了
//所以我们采用递归的思路,防止数组长度小于k/2,所以每次比较min(k/2,数组长度较小的那个),把小的那个
//对应的数组的数字排除,将两个新数组进入递归,并且k要减去排除的数字的个数。递归的出口是当k=1,或一个数组空了
//为什么奇数偶数个对这个算法没有影响呢,如果是最后求两个中间数的和除以2怎么办?!!!!!!!
//求两次
public double findMedianSortedArrays01(int[] nums1,int[] nums2) {
int n=nums1.length;
int m=nums2.length;
int left=(n+m+1)/2;
int right=(n+m+2)/2;
//将奇数和偶数合并,如果是奇数,会求两次同样的k
return (getKth(nums1,0,n-1,nums2,0,m-1,left)+getKth(nums1,0,n-1,nums2,0,m-1,right))*0.5;
}
private int getKth(int[] nums1,int start1,int end1,int[] nums2,int start2,int end2,int k) {
int len1=end1-start1+1;
int len2=end2-start2+1;
//让len1的长度小于len2,这样就能保证如果有数组空了,一定是len1
if(len1>len2)return getKth(nums2,start2,end2,nums1,start1,end1,k);
//如果len1<len2,把他们对调
if(len1==0)return nums2[start2+k-1];
if(k==1)return Math.min(nums1[start1],nums2[start2]);
int i=start1+Math.min(len1,k/2)-1;//第6位的索引是5
int j=start2+Math.min(len2,k/2)-1;
if(nums1[i]>nums2[j]) {
return getKth(nums1,start1,end1,nums2,j+1,end2,k-(j-start2+1));
}else {
return getKth(nums1,i+1,end1,nums2,start2,end2,k-(i-start1+1));
}
}//时间复杂度O(log(m+n)),用到了递归是尾递归,空间复杂度O(1)
//中位数,切割法
//一个长度为m的数组,有0-m个位置可以切
//把AB数组分别在i,j位置进行切割,将i的左边和j的左边组合成左半部分,
//j的右边和i的右边组合成右半部分
//i+j = m-i + n-j , 也就是 j = (m+n)/2 - i
//左半部分最大的值小于等于右半部分最小的值,max(A[i-1],b[j-1])<=min(A[i],B[j])
//那么中位数就是(左半部分最大值+右半部分最小值)/2
//A数组和B数组的总长度是奇数时,如果能够保证左半部分的长度比右半部分大1,
//左半部分的最大值小于等于右半部分的最小值的,那么中位数就是左半部分最大值
//上边的第一个条件可以合并我j=(m+n+1)/2-i,因为如果m+n是偶数,由于我们取的是int值,
//所以加1也不会影响结果,当然,由于0<=i<=m,为了保证0<=j<=n,必须保证m<=n
这个代码没看懂,,,,,,,,,,,,,,,,,,,,,!!!!!!
public double findMedianSortedArrays02(int[] A,int[] B) {
int m=A.length;
int n=B.length;
if(m>n) return findMedianSortedArrays(B,A);//保证m<=n
int iMin=0,iMax=m;
while(iMin<=iMax) {
int i=(iMin+iMax)/2;
int j=(m+n+1)/2-i;
if(j!=0&&i!=m&&B[j-1]>A[i]) {//i需要增大
iMin=i+1;
}else if(i!=0&&j!=n&&A[i-1]>B[j]) {//i需要减小
iMax=i-1;
}else{//达到要求,并且将边界条件列出来单独考虑
int maxLeft=0;
if(i==0) {maxLeft=B[j-1];}
else if(j==0) {maxLeft=A[i-1];}
else { maxLeft = Math.max(A[i-1], B[j-1]); }
if ( (m + n) % 2 == 1 ) { return maxLeft; } // 奇数的话不需要考虑右半部分
int minRight = 0;
if (i == m) { minRight = B[j]; }
else if (j == n) { minRight = A[i]; }
else { minRight = Math.min(B[j], A[i]); }
return (maxLeft + minRight) / 2.0; //如果是偶数的话返回结果
}
}
return 0.0;
}