剑指Offer:旋转数组的最小数字
2018-01-24 16:13
344 查看
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个已排序(由小到大)数组的一个旋转数组,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转数组,该数组的最小值为1。
对于在数组中找最小数字的问题最容易想到也是最容易实现的就是遍历一遍,时间复杂度为O(n),但是这并没有利用旋转数组的特点(两部分增序排列),无法让面试官满意。
拿题目中的例子来说,{3,4,5,1,2}中划分为两部分{3,4,5}和{1,2}这两部分都是增序的,不难看出最小的数字就是这两部分的分界。我们可以用二分查找法很快的找出最小值。
下面通过图解了解一下解法流程:
运行结果:
当然是了,只是旋转0个数字罢了。
如果{1,2,3,4,5}是旋转数组那么版本1.0的解法就失效了:
我们应该判断一下旋转数组是否是已经排序的,如果是,那么第一个元素就是最小的。因为原数组是排序好的旋转数组是否是原数组的判断规则就是arr[0]<arr[length-1]。
arr[left]、arr[right]和arr[mid]都是1,我们根本没有办法确定arr[mid]是属于前半部分还是后半部分。我们无法使用二分法缩小查找范围了,使用最原始的顺序查找法。
对于在数组中找最小数字的问题最容易想到也是最容易实现的就是遍历一遍,时间复杂度为O(n),但是这并没有利用旋转数组的特点(两部分增序排列),无法让面试官满意。
拿题目中的例子来说,{3,4,5,1,2}中划分为两部分{3,4,5}和{1,2}这两部分都是增序的,不难看出最小的数字就是这两部分的分界。我们可以用二分查找法很快的找出最小值。
下面通过图解了解一下解法流程:
版本1.0:初步解法
public class RotationArrayAns { public static void main(String[] args) { int[] arr = {3,4,5,1,2}; System.out.println(Min(arr)); } public static int Min(int[] arr){ if(arr==null){ throw new RuntimeException("Array is invalid!"); } int left = 0; int right = arr.length-1; while(right-left!=1){ int mid = (left+right)/2; if(arr[mid]>=arr[left]){ left = mid; }else{ if(arr[mid]<=arr[right]){ right = mid; } } } return arr[right]; } }
运行结果:
版本1.1:改进解法
问:{1,2,3,4,5}是{1,2,3,4,5}的旋转数组吗?当然是了,只是旋转0个数字罢了。
如果{1,2,3,4,5}是旋转数组那么版本1.0的解法就失效了:
我们应该判断一下旋转数组是否是已经排序的,如果是,那么第一个元素就是最小的。因为原数组是排序好的旋转数组是否是原数组的判断规则就是arr[0]<arr[length-1]。
public static int Min(int[] arr){ if(arr==null){ throw new RuntimeException("Array is invalid!"); } int left = 0; int right = arr.length-1; int mid = left; while(arr[left]>=arr[right]){//无法进入循环就说明旋转数组就是原数组 if(right-left==1){ mid = right; break; } mid = (left+right)/2; if(arr[mid]>=arr[left]){ left = mid; }else{ if(arr[mid]<=arr[right]){ right = mid; } } } return arr[mid]; }
最终解法
考虑数组{0,1,1,1,1}的旋转数组{1,0,1,1,1}使用上述解法,查找出错了。arr[left]、arr[right]和arr[mid]都是1,我们根本没有办法确定arr[mid]是属于前半部分还是后半部分。我们无法使用二分法缩小查找范围了,使用最原始的顺序查找法。
public static int Min(int[] arr){ if(arr==null){ throw new RuntimeException("Array is invalid!"); } int left = 0; int right = arr.length-1; int mid = left; while(arr[left]>=arr[right]){//无法进入循环就说明旋转数组就是原数组 if(right-left==1){ mid = right; break; } mid = (left+right)/2; if(arr[left]==arr[mid]&&arr[mid]==arr[right]){//无法使用二分查找,使用顺序查找 return MinInOrder(arr,left,right); } if(arr[mid]>=arr[left]){ left = mid; }else{ if(arr[mid]<=arr[right]){ right = mid; } } } return arr[mid]; } //顺序查找 private static int MinInOrder(int[] arr,int left,int right) { int min = arr[left]; for(int i=left;i<=right;i++){ if(min>arr[i]){ min = arr[i]; } } return min; }
相关文章推荐
- 【剑指offer】旋转数组中的最小数字
- 剑指offer—旋转数组的最小数字
- 剑指offer_6 旋转数组最小的数字
- 【剑指offer】面试题11:旋转数组的最小数字
- [剑指offer]面试题8:旋转数组的最小数字
- 剑指offer(旋转数组中的最小的数字)
- [剑指Offer]10.旋转数组的最小数字
- 剑指offer题解C++【6】旋转数组的最小数字
- 剑指offer-面试题8-旋转数组的最小数字
- 剑指offer-面试题8:旋转数组中的最小数字
- 剑指offer-面试题8:旋转数组的最小数字
- 剑指offer--旋转数组的最小数字
- 剑指offer:08 旋转数组的最小数字
- 剑指offer第六题【旋转数组的最小数字】c++实现
- 【剑指offer】旋转数组的最小数字&&二分查找
- [剑指offer][面试题08]求旋转数组的最小数字
- 【剑指Offer面试题】 九度OJ1386:旋转数组的最小数字
- (六)剑指offer之旋转数组的最小数字
- 剑指Offer--008-旋转数组的最小数字
- 剑指offer - 旋转数组的最小数字