33.Search in Rotated Sorted Array
题目描述:
给定一个被翻转的整型升序数组nums,数组中无重复元素,如[4,5,6,7,0,1,2
],和一个整数target。要求在被翻转过的数组中找到target的位置,若不存在,则返回-1。并且算法的时间复杂度要求为O(log n)。
自己的思路:
如果是有序数组,则使用二分查找就可以将复杂度控制在O(log n)了。所以问题集中在找到翻转中轴,即找到数组中最小值的位置。但如何在O(log n)的复杂度之下找到最小值的位置呢,这也是我被卡住的地方。
解题思路:
使用二分法查找最小值的位置,下面是具体实现的代码:
public int searchMin(int[] nums){
int left=0, right=nums.length-1;
while (leftnums[right]) left=mid+1;
else right=mid;
}
return left;
}
这里有一点和常规的二分法查找不同,就是当nums[mid]>nums[right]时,left等于mid+1,而当nums[mid]<=nums[right]时,right是等于mid的。这是因为当nums[mid]>nums[right]时,最小值不可能在mid位置,所以左指针可以直接跳到mid+1;但当nums[mid]<=nums[right]时,最小值可能就在mid位置上,所以右指针不能条到mid-1,而是应跳到mid位。
在看了推荐解题算法的大致思路后自己尝试写的时候就是卡在了这一点上。
下面是完整的实现代码:
class Solution { public int search(int[] nums, int target) { int pos = -1; if(nums.length==0) return pos; if(nums[0]]){ return binarySearch(nums, 0, nums.length-1, target); } int min = searchMin(nums); //System.out.println(min); if(target nums[nums.length-1]) return -1; if(target==nums[0]) return 0; if(target==nums[nums.length-1]) return nums.length-1; if(target>nums[0]){ pos = binarySearch(nums,0,min-1, target); }else { pos = binarySearch(nums,min,nums.length-1, target); } return pos; } /** * 用二分法在升序或降序数组中寻找target * @param nums * @param start * @param end * @param target * @return */ public int binarySearch(int[] nums, int start, int end, int target){ while (start<=end){ int mid = (start+end)/2; if(target == nums[mid]) return mid; if(nums[mid]>target) end=mid-1; else start=mid+1; } return -1; } /** * 找出数组nums中最小值的位置 * @param nums * @return */ public int searchMin(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 right=mid; } return left; } }
81.搜索旋转排序数组 ||
题目描述:
和上题一样,只是数组nums中的元素可以重复。
解题思路:
不知道怎么做,直接看的(能看明白的= =)题解。思路是直接使用改进的二分法查找。
二分查找的思想是不断对半缩小target所处的范围,但是当数组被翻转时,数组就不是完全有序了,此时要使用二分查找的话,需要在nums[mid]和target的比较之前明确mid位于翻转点的左侧还是右侧。如果mid位于翻转点的左侧,则mid左侧的数组是有序的,可以判断target是否在nums[start]和nums[mid]之间,若是,则end=mid-1,若非,start=mid+1;若mid位于翻转点的右侧,则mid右侧的数组是完全有序的,此时可以判断target是否在nums[mid]和nums[end]之间,若是,start=mid+1,若非,end=mid-1。
下面是完整的代码:
public boolean search_2(int[] nums, int target){ int start=0, end=nums.length-1, mid; while (start<=end){ mid = (start+end)/2; if(nums[mid] == target) return true; //重要!!以防碰到{1,3,1,1,1,1}这种情况 while (start<=end && nums[start] == nums[end] && nums[start] == nums[mid]){ start++; end--; } if(start>end) return false; //mid在旋转点左边 if(nums[mid] >= nums[start]){ //target小于mid if(target=nums[start]) end = mid-1; //target大于mid else start = mid+1; } //mid在旋转点右边 else { System.out.println(end); //target大于mid if(target>nums[mid] && target<=nums[end]) start = mid+1; else end = mid-1; } } return false; }
O(log n)理解:
对时间复杂度的推导有一点不是很明白,就是log n的复杂度是怎么回事。下面这个场景可以帮助理解,可以理解为不断地取1/2,所以折半查找算法的时间复杂度就是O(log n)。
场景:给小灰一条长16寸的面包,小灰每5天吃掉面包剩余长度的一半,第一次吃掉8寸,第二次吃掉4寸,第三次吃掉2寸......那么小灰把面包吃得只剩下1寸,需要多少天呢?
这个问题翻译一下,就是数字16不断地除以2,除几次以后的结果等于1?这里要涉及到数学当中的对数,以2位底,16的对数,可以简写为log16。
因此,把面包吃得只剩下1寸,需要 5 X log16 = 5 X 4 = 20 天。
如果面包的长度是 N 寸呢?
需要 5 X logn = 5logn天,记作 T(n) = 5logn。