目录
题目
- 原题连接:33. 搜索旋转排序数组
- 参考视频:B站教学视频
1-思路
1-1 模式识别:
- 模式1 ——>
**O(log n)**
:使用O(log n)
时间复杂度算法查找到目标值,因此想到的算法是二分算法。 - 模式2 ——> 旋转数组:旋转数组无法通过一次二分找到目标值,因此需要两次二分。
1-2 二分模板 && 本质
- 根据二分查找的过程,二分模板分两种情况。
- 一种是二分出 红色边界
target / x
,条件为q[mid]<=x
——>mid = (l+r+1)/2
- 一种是二分出 绿色边界
target / x
,条件为q[mid]>=x
——>mid = (l+r)/2
- 一种是二分出 红色边界
二分红色边界
- 结果收集
r
while(l<r){
int mid = (l+1+r)/2;
if(q[mid]<=x) l=mid;
else r = mid-1;
}
二分绿色边界
- 结果收集
l
while(l<r){
int mid = (l+r)/2;
if(q[mid]>=x) r=mid;
else l = mid+1;
}
- 二分的本质在于让 二分的区间满足一个性质。
1-3 本题思路
- 本题提供的旋转数组的分布如下,因此需要分别使用两次二分
①二分出第一个区间
-
- 答案区间
l = 0, r = nums.length-1
- 答案区间
-
- 利用二分性质,使得 前半段的性质 满足
nums[mid] >= nums[0]
,二分出旋转点
- 利用二分性质,使得 前半段的性质 满足
-
- 区间划分
- 若满足
nums[mid] >= nums[0]
此时 答案肯定在[mid,r]
中 - 否则答案在
[0,mid-1]
中,因为是r = mid-1
,此时 mid 初始化为mid=(l+r+1)/2
// 1. 第一次二分找出 分界点
// 二分本质,找到前半段 满足 nums[mid] >= nums[0]
int l = 0,r = nums.length-1;
while(l<r){
int mid = (l+r+1)/2;
if(nums[mid] > nums[0]) {
l = mid;
}
else {
r = mid-1;
}
}
②判断 target 在哪个区间
// 2.判断二次二分边界
if(target >= nums[0])
{
l = 0;
}
else {
l=r+1 ;
r = nums.length-1;
}
③利用二分性质
// 3.第二次二分
while(l<r){
int mid = (l+r+1)/2;
if(nums[mid]<=target) l=mid;
else r = mid-1;
}
return nums[r] == target ? r:-1;
2- 实现
⭐33. 搜索旋转排序数组——题解思路
:::info
- 1. 利用二分性质,二分出分界点
- 二分本质是使得一个区间满足性质
- 首次二分使得
**nums[mid]**
满足**nums[mid]>=nums[0]**
的性质 **if(nums[mid] >= nums[0])**
此时 结果在**[mid,r]**
因此 ——>**l = mid**
- 否则
r = mid-1
,根据判断**mid =(l+r+1)/2**
- 2. 利用边界找出二次二分边界
**if(target >= nums[0]) ——> l = 0;**
**else l = mid+1; r = nums.length-1**
- 3. 二次二分
- 利用二分红色边界模板
:::
- 利用二分红色边界模板
class Solution {
public int search(int[] nums, int target) {
// 1. 第一次二分找出 分界点
// 二分本质,找到前半段 满足 nums[mid] >= nums[0]
int l = 0,r = nums.length-1;
while(l<r){
int mid = (l+r+1)/2;
if(nums[mid] > nums[0]) {
l = mid;
}
else {
r = mid-1;
}
}
// 2.判断二次二分边界
if(target >= nums[0])
{
l = 0;
}
else {
l=r+1 ;
r = nums.length-1;
}
// 3.第二次二分
while(l<r){
int mid = (l+r+1)/2;
if(nums[mid]<=target) l=mid;
else r = mid-1;
}
return nums[r] == target ? r:-1;
}
}
3- ACM实现
public class findTraversalNums {
public static int findT(int[] nums,int target){
int l = 0 , r = nums.length;
// 1.首次二分
while(l<r){
int mid = (l+r+1)/2;
if(nums[mid] >= nums[0]) l=mid;
else r=mid-1;
}
// 2. 划分区间
if(target >= nums[0]) l = 0;
else{
l = r+1;
r = nums.length-1;
}
// 2.二次二分
while(l<r){
int mid = (l+r+1)/2;
if(nums[mid] <= target){
l = mid;
}else{
r = mid-1;
}
}
return target == nums[r] ? r:-1;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("输入旋转数组长度");
int n = sc.nextInt();
int[] nums = new int[n];
for(int i = 0 ; i < n;i++){
nums[i] = sc.nextInt();
}
System.out.println("输入目标值");
int target = sc.nextInt();
System.out.println("目标下标为"+findT(nums,target));
}
}