[Leetcode] 旋转问题(旋转数组的查找,旋转list,旋转矩阵)
[1] Search in Rotated Sorted Array
在二分查找问题当中,对于边界的查找,尤其是==的情况应该放到左边界上,因为对于mid的求取又是就是left,所以这是检测应该是true,所以下面的nums[mid]>=nums[left],如果改为nums[mid]>nums[left]就会出现问题。
1 public class Solution { 2 public int search(int[] nums, int target) { 3 int left =0,right = nums.length-1; 4 while(left<=right){ 5 int mid = left + (right-left)/2; 6 if(nums[mid]==target) return mid; 7 if(nums[mid]>=nums[left]){//left part is sorted 8 if(nums[left]<=target&&target<nums[mid]) right=mid-1; 9 else left=mid+1; 10 }else{//right part is sorted 11 if(nums[mid]<target&&target<=nums[right]) left=mid+1; 12 else right=mid-1; 13 } 14 } 15 return -1; 16 } 17 }
假设按照上面的错误思路改为
1 public class Solution { 2 public int search(int[] nums, int target) { 3 int left =0,right = nums.length-1; 4 while(left<=right){ 5 int mid = left + (right-left)/2; 6 if(nums[mid]==target) return mid; 7 if(nums[mid]>nums[left]){//left part is sorted 8 if(nums[left]<=target&&target<nums[mid]) right=mid-1; 9 else left=mid+1; 10 }else{//right part is sorted 11 if(nums[mid]<target&&target<=nums[right]) left=mid+1; 12 else right=mid-1; 13 } 14 } 15 return -1; 16 } 17 }
具体问题是如何发生的,我们举个例子
[3 1] 1
left =0 ,right=1, mid=0, 红色部分判断为false, 所以right part is sorted
这是判断 target没有在有序的部分,所以 right--;
那么之后就永远找不到1了。
[2] Search in Rotated Sorted Array II
遇到这个问题,显然问题的难点是在相同元素的干扰上,如果直接去想如何处理这些复杂的问题,可能会陷入非常复杂的思维误区。我们可以从上面的针对没有重复元素的情况进行简单的改进。
由于这时又重复元素,所以if(nums[mid]>=nums[left]){//left part is sorted这条语句失效,因为会遇到下面的情况
1 2 1 1 1,中间的1等于左边的1,所以认为左边是有序的,但是这里并不是这种情况,在第一个问题当中出现这种情况的原因是mid和left是相等的情形,在这里不仅如此。
所以需要对这个问题进行分解.分等于和不等于两种情况,显然不等于的时候,原来的假设仍然是成立的。
1 public class Solution { 2 public static boolean search(int[] A, int target) { 3 int left=0,right=A.length-1; 4 while(left<=right){ 5 int mid =(right+left)/2; 6 if(A[mid]==target) return true; 7 if(A[mid] < A[right]){// right part is sorted 8 if(target>A[mid]&&A[right]>=target){ 9 left=mid+1; 10 }else{ 11 right=mid-1; 12 } 13 }else if(A[mid] > A[right]){//left part is sorted 14 if(target>=A[left]&&A[mid]>target){ 15 right=mid-1; 16 }else{ 17 left=mid+1; 18 } 19 }else{ 20 if(A[left]!=target) left++; 21 if(A[right]!=target) right--; 22 } 23 } 24 return false; 25 } 26 }
[3] Find Minimum in Rotated Sorted Array
6,7,8,9,1,2,3,4,5
我们维持两个指针,left 指向左边的递增数组,right指向右边的递增数组,通过不断地向中间移动两个指针,最后left指向左边递增数组的最后一个元素,right指向右边递增数组的第一个元素。
最后的判停条件式right-left==1.
int nums[size];int left=0,right=size-1;
int mid=0;
while(nums[left]>nums[right]){
//如果不对right-left==1这个条件进行判断,可能会出现特殊的情况
if(right-left==1) {
mid=right;
return right;
}
int mid = (left+right)/2;
if(nums[mid]>nums[left]) left=mid;//左半边是sorted,最小元素一定在右边,切中间元素在右边
else right=mid;//右半边是sorted,最小元素一定在左边,且中间元素在左边
}
return mid;
[4] Find Minimum in Rotated Sorted Array II
同样的道理,这时由于相同元素的出现,上面的判断将会失效。
比如 2 2 2 2 1 或者 2 1 2 2 2
会判定最小元素在左边,实际上是错误的。
1 public static int findMin(int [] num) { 2 int left = 0,right = num.length - 1; 3 int mid = 0; 4 //当while循环的条件不满足的时候,实际上当前的数组应该是有序的数组了 5 //如果刚开始就是有序的,那么放回mid=0的值就可以 6 //否则在while循环loop当中总会保持两者一个在右边的有序数组当中,另一个在左边的有序数组当中 7 while(num[left] >= num[right]){ 8 // 分界点 9 if(right - left == 1){ 10 mid = right; 11 break; 12 }//if 13 mid = left + (right - left) / 2; 14 15 //为下面的情况,那么无法判断中间mid在那一方,所以直接linear search 16 if(num[left] == num[right] && num[left] == num[mid]){ 17 return linearsearch(num,left,right); 18 } 19 if(num[mid] >= num[left]){ 20 left = mid; 21 } 22 else{ 23 right = mid; 24 } 25 } 26 return num[mid]; 27 }
[5] Rotate Array
1 2 3 4 5 6 7 -> 4 5 6 7 1 2 3
1. 3 2 1 7 6 5 4
2. 4 5 6 7 1 2 3
[6] Rotate List
在相应位置截断
[7] Rotate Image
首先上下翻转,然后绕主对角线翻转