LeetCode--33. Search for a Range ,附加153、154、33、81(二分法)
问题33 大意:在一个排好序的数组中寻找target的起始位置。 target在数组中不一定只有一个。结果返回形式int[] {start,end},如果不存在 返回{-1,-1}
要求时间复杂度O(logn)
方法:在数组中找target最左边的位置和最右边的位置,使用二分法.
下面函数为二分法的变体
注意:
1. 考虑当 low == high 时, 我们希望游标往哪里走。
2. 考虑返回的下标,是start、end 还是mid
public static int searchl(int[] nums, int target){ int len = nums.length; int start = 0 ; int end = len -1 ; int mid = 0 ; while(start <= end ){ mid = (end+start)/2; if(nums[mid]<target) start = mid+1; //mid元素小于target,可以令start=mid+1 else end = mid-1 ; } if(len==0||start>=len||nums[start]!=target) return -1; return start; } public static int searchr(int[] nums, int target){ int len = nums.length; int start = 0 ; int end = len -1 ; int mid = 0 ; while(start <= end ){ mid = (end+start)/2; if(nums[mid]>target) end = mid-1 ; else start = mid+1; } if(len==0||end<0||nums[end]!=target) return -1; return end; }
方法体:
public static int[] searchRange(int[] nums, int target) { int sl = searchl(nums,target); int sr = searchr(nums,target); return new int[] {sl,sr}; }
153: Find Minimum in Rotated Sorted Array
154: Find Minimum in Rotated Sorted Array II
第一个问题:给定一个有序数组旋转后的数组,如 (i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2).求出数组中最小的数。(数组中无重复元素)
方法:二分法。 start=0 ; end=len-1;
1. mid=(start+end)/2;
2. 如果nums[mid]>nums[start],说明start到mid是递增的,让start=mid;
3. 如果nums[mid]<nums[end],说明mid到end是递减的,让start=mid;
1 2 3循环,直到start+1 = end; 这样start指向的是最大的元素,end指向最小的元素。
public static int findMin(int[] nums) { int len = nums.length; int start = 0 ; int end = len-1 ; if(nums[start]<=nums[end]) //表示单调递增的情况 return nums[start]; int mid ; while(start+1<end){ mid= (start+end)/2; if(nums[mid]>nums[start]){ //若中间的元素大于start元素,令start=min,意思就是start一直往大的数字走 start = mid; } else if(nums[mid]<nums[end]){ //若中间的元素小于start元素,令end=min,意思就是end一直往小的数字走 end = mid; } } //要求的最小元素的位置就在start的下一个,(也就是end) 因为数组是旋转的,所以最大的下一个就是最小的 return nums[end]; }
另一种方法:
public int findMin(int[] nums) { int len = nums.length; int start = 0 ; int end = len-1 ; int mid ; while(start<end){ if(nums[start]<nums[end]){ return nums[start]; } mid= (start+end)/2; if(nums[mid]>=nums[start]){ start = mid+1; } else if(nums[mid]<nums[end]){ end = mid; } } return nums[start]; }
第二个问题:类似第一个,只是数组中可能有重复元素。
例如[3,1,3,3,3],这种情形,在第一次循环时 start end mid位置的元素均相同,这样就没有办法判断哪边是递增的哪边是递减的,因此在此情形二分法失效,只能使用顺序搜索。
可以先找到start和end的位置,然后对start和end之间的元素进行顺序搜索。
public static int findMin(int[] nums) { int len = nums.length; int start = 0; int end = len - 1; if (nums[start] < nums[end]) // 表示单调递增的情况 return nums[start]; int mid = 0; while (start + 1 < end) { mid = (start + end) / 2; if(nums[mid]==nums[start]&&nums[mid]==nums[end]){ break; } if (nums[mid] >= nums[start]) { start = mid; } else if (nums[mid] <= nums[end]) { end = mid; } } if(nums[mid]==nums[start]&&nums[mid]==nums[end]){ int min = nums[start]; for(int i = start+1 ; i <=end ; i++){ if(min>nums[i]) min = nums[i]; } return min; } return nums[end]; }
也可以使用另一种方法:每次在开始前,将重复的元素跳过。然后按照一般的方法进行即可(注意如果头部和尾部恰好是相同的,如334123333这种情况,只要变成34123即可,因为按照递增的原则,最小值一定在左侧即3-1)。当然也可以加上while(nums[start]==nums[end]) start++;
public static int findMin(int[] nums) { int len = nums.length; int start = 0; int end = len - 1; if (nums[start] < nums[end]) // 表示单调递增的情况 return nums[start]; int mid = 0; while (start + 1 < end) { while(start<len-1&&nums[start]==nums[start+1]) start++; while(end>0&&nums[end]==nums[end-1]) end--; mid = (start + end) / 2; if (nums[mid] > nums[start]) { start = mid; } else if (nums[mid] < nums[end]) { end = mid; } } return nums[end]; }
33 Search in Rotated Sorted Array II
public static int search(int[] nums, int target) { int len = nums.length; if(len==0) return -1; int start = 0 ; int end = len-1; int mid; while(start <= end){ mid = (start+end)/2; if(nums[mid]==target) return mid; if(nums[mid]>=nums[start]){ if(target>=nums[start]&&target<=nums[mid]) end = mid-1; else start = mid+1; } else if(nums[mid]<=nums[end]){ if(target>=nums[mid]&&target<=nums[end]) start = mid+1; else end = mid-1; } } return -1; }
81 Search in Rotated Sorted Array II
public static boolean search(int[] nums, int target) { int len = nums.length; if(len==0) return false; int start = 0 ; int end = len-1; int mid; while(start <= end){ while(start<len-1&&nums[start]==nums[start+1]) start++; while(end>0&&nums[end]==nums[end-1]) end--; mid = (start+end)/2; if(nums[mid]==target) return true; if(nums[mid]>=nums[start]){ if(target>=nums[start]&&target<=nums[mid]) end = mid-1; else start = mid+1; } else if(nums[mid]<=nums[end]){ if(target>=nums[mid]&&target<=nums[end]) start = mid+1; else end = mid-1; } } return false; }

浙公网安备 33010602011771号