您的位置:首页 > 其它

153 Find Minimum in Rotated Sorted Array

2017-03-18 07:50 309 查看
接着上面的两道题,仍然在这个rotated sorted array中变换出题。这道题是要找出最小的那个数(当然也可以是最小数的index)

之前写的代码如下,还包括了好长的思考比较,唉,一开始我还真是非常认真,非常耐心去抠细节的:

public class Solution {
public int findMin(int[] nums) {
// 1.24 做firstBadVersionn那道题时,总结出二分法的一些内涵本质的东西,那道题和这道非常像,都不是普通的找target的二分法,而是边界类型的二分法,所以赶紧来尝试下
int left =0;
int right = nums.length-1;
//if(nums[left]<nums[right]) return nums[left];
while(left<right){
if(nums[left]<nums[right]) return nums[left];
int mid=(left+right)/2;
if(nums[mid]>=nums[left]) left=mid+1; // 如果左边是sorted,那么目标min肯定不在这个范围,更新left=mid+1,left可能是目标min
else right=mid; // 否则的话,右边是sorted,那么mid可能是最小值,更新right=mid,right也可能是最小值
} // 所以当最后缩小到 left==right,也就是while loop跳出时的条件,那么就只剩下这一个了,返回[left]和[right]都可以

return nums[right];
// 1.24: 总结一下吧。。。debug了几次才通过这道题。。。主要原因是没有充分发现这道题的具体情况,而只是光光想着要用binary search中的技巧。。。
// 我事先只想了不断靠近left 和 right两个游标。。。而且update的方法也想好了,就是如果[mid]大于[left]那么就右移+1,如果[mid]<[left]那就right=mid,这就保证了left的左边肯定不是target,但是right就是可能的target。。
// 以上逻辑一个点很对,就是夹逼法。。。一定保证了target在left和right之间
// 但是出现很大的错误:1,忽略了最重要的一点,那就是如果[left]<[right]了,那就可以返回了 [left]了啊,想想如果一开始就是顺序排列好的直角三角形。。。斜边为递增的value...所以要把这个判断放在while里的第一句
// 2,我之前的if判断是把[mid] 和 [0] 进行比较了。。这其实是可以的,没有错,但是因为我没写1中的决定性的判断语句,所以还以为这里是个bug,事实证明,把[mid] 与[0], [left] 比较都没有问题,因为 [0] 本身就是 悬崖断崖 的分界点 位置。。。在ppt里去画个图吧。。。
// 最为关键的一点是,没有考虑[mid]==[left] 这种情况,我以为不可能发生!!!这就是我漏掉的一个细节!!那就是当left和right相邻的时候就是会发生啊!!!发生了之后,left要+1啊,然后就while loop结束了。。。这就是个极大的疏忽。。。所以为了保证while内的 “判断条件完整性”,就是说一定要保证所有情况分支都必须 要改变 left和right,否则就是死循环出不去了。。。
// 看看我一道题,挖了多少东西出来

// 这个思路和下面的solution2基本一样,但是我自己的这个逻辑性更强,明显能体会到全面,和准确的代码布置。。精简很多

// 一下这个方法work,我是为了写上面的方法,才comment out
/* int len=nums.length;
if(len==1) return nums[0];

// solution1 1ms , only beat 5%
if(nums[0]<nums[len-1]) return nums[0]
4000
;
for(int i=1; i<len; i++){
if(nums[i]<nums[i-1]) return nums[i];
}
return -1;*/

/*
// solution2 0ms and beats 63%
// at least two elems in nums;
int low=0, high=len-1;
if(nums[low]<nums[high]) return nums[low];
while(low<high){
int mid=(low+high)/2; // 好好总结!!每次二分法的题,都要涉及细节。关键是mid=(low+high)/2 时,不断夹逼,只剩下相邻两项时,mid就是low的情况!!!所以你可以假设验证两个实例: 实例1:原本只有两个元素,互换了位置。。。看是否成立;实例2:最后剩下了中间位置/普遍情况 相邻两个元素的情况。。。实例3:最前面和最后面的情况。。。综上考虑,可以得出一下结论,就是一个出口。。
if(mid==low) return nums[high]; // high=low+1=mid+1; 已经相邻了。。。只剩下2个elem,那么必然是右边的
if(mid!=0 && nums[mid]<nums[mid-1]) return nums[mid]; // 其实这个mid!=0可以省略,因为什么情况下mid会等于0?只有剩下的位置是[0]和[1]时,这个[0]和[1]的情况在上面已经包括了。。
// 嗯嗯,我测试了一下,确实可以省略mid!=0 &&!!! 棒!!!
if(nums[mid]>nums[0]) low=mid;
else high=mid;
}
return -1;*/
}
}

// 这道题和真正的2分法查找,还是有点区别,就是在判断[mid]之后更新 low or high 时,是== mid呢 还是==mid+1 == mid-1;还有就是while(low<high) 还是 while(low==high);
// 在二分查找时,用得是while(low<=high), 更新是+1;本题中是while(low<high), 更新是==,这两者是相互搭配的。。。本题这么设置这两个条件,还有一个关键/一个必然要写的,就是mid==low的情况,试想当最后只有2个相邻元素时,那mid=low,如果配上更新条件使 low=mid,那就是个 infinite loop, mid=low 然后 low= mid,low永远没变化,跳不出while(low<high) 这个循环。。。



捂住眼睛不看,今天自己再写一遍:

public class Solution {
public int findMin(int[] nums) {
int left=0;
int right=nums.length-1;
if(nums[left]<nums[right]) return nums[left]; // 如果初始情况就是sorted,那直接return

while(left<right){ // 退出条件是left==right,这是跟据我一下的判断条件来的:一直保持left和right都有可能是min的index
/* if(nums[left]<nums[right]) return nums[left];
int mid=(left+right)/2;
if(nums[mid]>=nums[left]) left=mid+1; // 如果是判断 mid和left,那么一定要加=,因为可能是相邻了,
同时要加:if(nums[left]<nums[right]) return nums[left]; 为了判断只剩下两个数时,是否sorted,因此只有在这里不return时,一下的不走才成立。
else right=mid;*/

int mid=(left+right)/2;
if(nums[mid]<nums[right]) right=mid;
else left=mid+1; // 判断右边方便得多!而且性能高!!以后用判断右边的方法,摆脱left和right相邻是 [left]==[mid] 的情况

}
return nums[left];
}
}

以后都判断mid 和 right,摆脱了left和right相邻时,[mid]==[left]的情况,代码简单性能高。
下一篇我要改写一些search in rotated sorted array II 中的代码,也是从right判断,不是从left。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: