文章目录
前言
1、寻找旋转排序数组中的最小值
2、寻找旋转排序数组中的最小值 II
3、搜索旋转排序数组
4、搜索旋转排序数组 II
5、搜索旋转数组
6、在排序数组中查找元素的第一个和最后一个位置
一、寻找旋转排序数组中的最小值(力扣154)
分析:
参考
大佬题解
关键部分:
之前总觉得二分必须是递增序列才能用 固化思维 左<中<右,跳出这个固定思维圈,更好理解
class Solution {
public int findMin(int[] nums) {
//数组元素最小值
int left =0;
int right =nums.length-1;
int mid;
int res = 0;
while(left<=right){
mid = left +(right-left)/2;
if(nums[mid]<nums[right]){
right=mid;
}else {
res = left;
left=mid+1;
}
}
return nums[res];
}
}
二、寻找旋转排序数组中的最小值 II(154)
尽可能减少整个操作步骤
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length-1;
int mid;
int res=0;
while(left<=right){
mid = left+(right-left)/2;
if(nums[mid]<nums[right]){
//缩小右边界
right = mid;
}else if(nums[mid]>nums[right]){
res = left;
left = mid+1;
}else if(nums[mid]==nums[right]){
right--;
}
}
return nums[left];
}
}
三、搜索旋转排序数组(力扣33)
分析:
根据“寻找旋转排序数组中的最小值”找到最小值的下标位置,然后分为不同的情况划分区间进行二分查找
class Solution {
public int search(int[] nums, int target) {
//先找到最小值所对应的下标
int left = 0;
int right = nums.length-1;
int mid=0 ;
while(left<=right){
mid = left+(right-left)/2;
if(nums[mid]<nums[right]){
right = mid;
}else{
left= mid+1;
}
}
//mid就是最小值的下标 判断[mid,nums.length-1] 里是否有目标值
if(nums[nums.length-1]<target && mid ==0){
return -1;
}else if(nums[nums.length-1]>=target ){
return findByErFen(nums,mid,nums.length-1,target);
}else if(mid!=0 && nums[nums.length-1]<target){
//[0,mid-1];
return findByErFen(nums,0,mid-1,target);
}
return -1;
}
public int findByErFen(int[] nums,int left,int right,int target){
int l=left;
int r=right;
int mid;
int ans =-1 ;
while(l<=r){
mid = l+(r-l)/2;
if(nums[mid]>target){
r=mid-1;
}else if(nums[mid]<target){
l=mid+1;
}else{
ans = mid;
return ans;
}
}
return ans;
}
}
四、搜索旋转排序数组 II(力扣81)
分析:
和33类似,但是需要注意的是,会有重复值的情况出现,因此需要对数据进行一些预处理
while (left <= right && nums[0] == nums[right]) {
right--;
}
if(right==-1) return nums[0]==target?true:false;
rightBorder = nums[right];
index=right;
class Solution {
public boolean search(int[] nums, int target) {
//可能会有相同值出现
//和1比较类似 但是有重复值的情况
int left = 0;
int right = nums.length-1;
int mid=0;
int res =0;
int rightBorder=Integer.MIN_VALUE;
int index = -1;
if(nums.length==1){
return nums[0]==target?true:false;
}
// 恢复二段性
while (left <= right && nums[0] == nums[right]) {
right--;
}
if(right==-1) return nums[0]==target?true:false;
rightBorder = nums[right];
index=right;
while(left<=right){
mid = left+ (right-left)/2;
if(nums[mid]<nums[right]){
right=mid;
}else if(nums[mid]>nums[right]){
left = mid+1;
}else if(nums[mid]==nums[right]){
right--;
}
}
//mid 就是此时最小值所在位置的下标
//进行二分查找target值
if(mid==0 && target>rightBorder){
return false;
}else if(target<=rightBorder){
return ErFen(nums,mid,index,target)==-1?false:true;
}else if(mid!=0 &&target>rightBorder){
return ErFen(nums,0,mid-1,target)==-1?false:true;
}
return false;
}
public int ErFen(int[] nums,int left,int right,int target){
int l=left;
int r = right;
int mid;
int ans = -1;
while(l<=r){
mid = l+(r-l)/2;
if(nums[mid]>target){
r=mid-1;
}else if(nums[mid]<target){
l=mid+1;
}else{
ans = mid;
return ans;
}
}
return ans;
}
}
五、搜索旋转数组(面试题 10.03.)
分析:
上面的几道题相比,注释里给出了几个重点改变
重点一:当left符合时直接返回, 因为找的是最小的索引
while (left <= right && nums[0] == nums[right]) {
right--;
}
if(right==-1) return nums[0]==target?0:-1;
重点二:当中间值等于目标值,将右边界移到中间,因为左边可能还有相等的值
while(l<=r){
mid = l+(r-l)/2;
if(nums[mid]>=target){
if(nums[mid]==target){
ans = mid;
}
r =mid-1;
}else if(nums[mid]<target){
l=mid+1;
}
}
class Solution {
public int search(int[] nums, int target) {
//可能会有相同值出现
//和1比较类似 但是有重复值的情况
int left = 0;
int right = nums.length-1;
int mid=0;
int res =0;
int rightBorder=Integer.MIN_VALUE;
int index = -1;
if(nums.length==1){
return nums[0]==target?0:-1;
}
if(nums[0]==target) return 0;
// 恢复二段性
while (left <= right && nums[0] == nums[right]) {
right--;
}
if(right==-1) return nums[0]==target?0:-1;
rightBorder = nums[right];
index=right;
while(left<=right){
mid = left+ (right-left)/2;
if(nums[mid]<nums[right]){
right=mid;
}else if(nums[mid]>nums[right]){
left = mid+1;
}else if(nums[mid]==nums[right]){
right--;
}
}
if(nums[mid] ==target) return mid;
//mid 就是此时最小值所在位置的下标
//进行二分查找target值
if(mid==0 && target>rightBorder){
return -1;
}else if(target<=rightBorder){
return ErFen(nums,mid,index,target)==-1?-1:ErFen(nums,mid,index,target);
}else if(mid!=0 &&target>rightBorder){
return ErFen(nums,0,mid-1,target)==-1?-1:ErFen(nums,0,mid-1,target);
}
return -1;
}
public int ErFen(int[] nums,int left,int right,int target){
int l=left;
int r = right;
int mid;
int ans = -1;
while(l<=r){
mid = l+(r-l)/2;
if(nums[mid]>=target){
if(nums[mid]==target){
ans = mid;
}
r =mid-1;
}else if(nums[mid]<target){
l=mid+1;
}
}
return ans;
}
}
六、在排序数组中查找元素的第一个和最后一个位置(力扣34)
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[2];
int leftBorder = searchLeftBorder(nums,target);
int rightBorder = searchRightBorder(nums,target);
if(rightBorder==-2 || leftBorder==-2 ){
res[0]=-1;
res[1]=-1;
return res;
}
if(rightBorder-leftBorder>1){
res[0]=leftBorder+1;
res[1]=rightBorder-1;
}else{
res[0]=-1;
res[1]=-1;
}
return res;
}
public int searchLeftBorder(int[] nums,int target){
int left = 0;
int right = nums.length-1;
int res =-2;
int mid;
while(left<=right){
mid = left+(right-left)/2;
if(nums[mid]>=target){
right = mid-1;
res = right;
}else{
left = mid+1;
}
}
return res;
}
public int searchRightBorder(int[] nums,int target){
int left = 0;
int right = nums.length-1;
int res =-2;
int mid;
while(left<=right){
mid = left+(right-left)/2;
if(nums[mid]>target){
right = mid-1;
}else{
left = mid+1;
res = left;
}
}
return res;
}
}