问题描述:
Supposea sorted array is rotated at some pivot unknown to you beforehand.
(i.e., 0 1 2 4 5 6 7
might become 4 5 6 7 0 1 2
).
You are given a target value to search. If found in the arrayreturn its index, otherwise return -1.
You may assume no duplicate exists in the array.
问题分析:
问题表示有一个原来有序,后来在某一位置反转的数组,查找某个值;
该题很容易想到《剑指offer》中的求旋转数组的最小值的问题,这里要更简单一点,因为不用考虑重复值问题;
原题可以转化为先求取旋转数组的旋转位置,然后将旋转后的数组想象成一个循环右移一定位数的有序数组,进行二分查找即可;
代码:
public class Solution { // 其实可以看做两个问题的组合,先找到反转位置,再进行二分查找 public intsearch(int[] nums, int target) { if (nums.length == 0) return -1; return binarySearch(nums, 0, nums.length - 1, target,getRotateIndex(nums, 0, nums.length - 1)); } // 找到反转的位置,即整个反转数组中的最小值 private int getRotateIndex(int[] nums, int start, int end) { int mid = start; while (start < end) { mid = start + (end - start) / 2; // 由于每个值都不同,中间值大于末尾值,则表示中间值位于前面的递增序列,则最小值一定在右半段中 if (nums[mid] > nums[end]) start = mid + 1; else // 注意如果[mid]值小于[end],可能[mid]就是最小值,故end = mid;而不是mid - 1 end = mid; } return start; } // 二分查找 private intbinarySearch(int[] nums, int start, int end, int target, int index) { int mid, read_mid; while (start <= end) { mid = start + (end -start) / 2; // 这里涉及到一个技巧,将反转的数组想象成一个循环数组,则相当于把0-n位循环左移了index位(0移动到了index); read_mid = (mid + index) % nums.length; if (target == nums[read_mid]) return read_mid; if (nums[read_mid] < target) start = mid + 1; else end = mid - 1; } return -1; } }
拓展:
《剑指Offer》 旋转数组的最小数字:
题目描述:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减序列的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
解题思路:
遍历的时间复杂度为O(n),性能较差;由于旋转数组是由两个递增数组(当旋转元素个数为0时,只有一个递增数组,要特殊处理此特殊情况)组成。
采用二分查找(双指针法)可以有效地优化时间复杂度;
情况一:
对于{3,4,5,1,2},采用双指针法,中间元素[mid]的值如果在前面的递增序列,则[mid]一定不小于[start];故可以缩小查找的范围:将start挪至mid处,因为最小值即为两个递增数组的分界点;
情况二:
对于{4,5,1,2,3},[mid]处于后面的递增序列,则[mid]一定不大于[end];此时可以缩小查找范围:将end挪至mid处
情况三:
此为特殊情况需要考虑:{2,2,2,2,1,2},此时[mid],[start],[end]值相同,无法判断是向左还是向右缩小窗口;故只能采取遍历的方法找到最小值;
情况四:
特殊情况:即0个元素反转时,此时应当满足[start]<[end](等于的情况可以认为已经做了反转),直接输出首元素即可;
代码:
public class Solution { public int minNumberInRotateArray(int [] array){ if (array.length == 0) return 0; int result = 0; int start = 0, end = array.length - 1; int mid = start; while (array[start] >= array[end]) { // 处理start = 2,end = 3,这种特殊情况 if (end - start <= 1) { mid= end; break; } mid= start + (end - start) / 2; // 如果出现头指针和尾指针,以及中间指针三个数字相同的情况,则需要顺序遍历 if ((array[start] ==array[mid])&&(array[end] == array[mid])){ result= array[start]; for (int i = start + 1; i <= end; i++) { if (array[i] < result) { result= array[i]; } } return result; } // 中间元素在左边递增序列中的情况 if (array[mid] >= array[start]) start= mid; if (array[mid] <= array[end]) end= mid; } return array[mid]; } }