算法-秒杀旋转数组问题
搜索旋转数组是一个非常经典的题目了,在Leetcode和剑指Offer上店铺有相应的问题,可以总结为:一个经过排序的数组,在某点发生了旋转,寻找里面是不是有对应的值
例如,原数组为12345,而经过旋转后的数组为34512,我们搜索目标值为4.一种简单暴力的做法就是遍历整个字符串,时间复杂度为O(N),看上去已经很完美了,然而,由于数组是部分有序的,我们还可以使用二分法搜索数组,使时间复杂度达到 log(N)。
由于二分法只能在有序数组中使用,因此,我们首先需要做的就是找到旋转点。
1、找到旋转点
2、将数组划分为两部分,分别用二分法搜索
其中寻找到旋转点的操作也必须是log(N)。
怎么判断他是不是旋转点呢? 其实也不难理解,旋转点的前面的值大于后面的值(除了初始位置外)
1、声明一个low和一个high指针,指向数组两侧。
2、计算中点位置,如果中点位置的元素大于high位置元素,说明旋转点在右侧。
3、如果小于high位置的元素,说明旋转点在左侧
4、如果等于high位置的元素,我们是无法确定旋转点在哪里的,这时候让high向左移动一步。这时候其实是有可能退化成O(N)的,例如111121这种情况。
1、搜索旋转数组II
知道了上述说明的点,看一个经典的题目:81. 搜索旋转排序数组 II
问题描述:
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。
编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。
示例 1:
输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true
示例 2:
输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false
经过上面的描述,答案已经呼之欲出了
public boolean search(int[] nums, int target) {
if(nums.length==0){
return false;
}
int low=0,high=nums.length-1;
//寻找旋转点
while(low<high){
int mid=(low+high)>>1;
if(nums[mid]<nums[high]){
high=mid;
}else if(nums[mid]>nums[high]){
low=mid+1;
}else{
while (high > low) {
if (nums[high - 1] > nums[high]) {
low = high - 1;
break;
}
high--;
}
}
}
return findTarget(nums,0,low-1,target)||findTarget(nums,low,nums.length-1,target);
}
public boolean findTarget(int nums[],int low,int high,int target){
if(low<0||high>nums.length-1){
return false;
}
while(low<=high){
int mid=(low+high)>>1;
if(nums[mid]>target){
high=mid-1;
}else if(nums[mid]<target){
low=mid+1;
}else {
return true;
}
}
return false;
}
2、搜索旋转数组I
33.搜索旋转数组I更简单,因为没有重复元素的情况,不会退化成O(N)
题目描述
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
public int search(int[] nums, int target) {
if(nums.length==0){
return -1;
}
int low=0,high=nums.length-1;
//寻找旋转点
while(low<high){
int mid=(low+high)>>1;
if(nums[mid]<nums[high]){
high=mid;
}else if(nums[mid]>nums[high]){
low=mid+1;
}else{//这里其实不需要判断了,因为在本题中没有重复元素
high--;
}
}
if(nums[nums.length-1]<target){
return findTarget(nums,0,low-1,target);
}else{
return findTarget(nums,low,nums.length-1,target);
}
}
public int findTarget(int nums[],int low,int high,int target){
if(low<0||high>nums.length-1){
return -1;
}
while(low<=high){
int mid=(low+high)>>1;
if(nums[mid]>target){
high=mid-1;
}else if(nums[mid]<target){
low=mid+1;
}else{
return mid;
}
}
return -1;
}
3、旋转数组的最小数字
搜索旋转数组的最小数字出自剑指Offer,其实本质上就是个求旋转点的问题,属于上面两个题的第一步。
public int minNumberInRotateArray(int [] array) {
if(array.length==0){
return 0;
}
int low=0,high=array.length-1;
while(low<high){
int mid=(low+high)>>1;
if(array[mid]>array[high]){//在最小值在右边
low=mid+1;
}else if(array[mid]<array[high]){//旋转点在左边
high=mid;
}else {
high--;
}
}
return array[low];
}