在有序旋转数组中找到一个数
题目描述
有序数组arr可能经过一次旋转处理,也可能没有,且arr可能存在重复的数。例如,有序数组[1, 2, 3, 4, 5, 6, 7],可以旋转处理成[4, 5, 6, 7, 1, 2, 3]等。给定一个可能旋转过的有序数组arr,再给定一个数num,返回arr中是否含有num
关于旋转操作:可以简单的理解为把序列从某个位置切成两段然后交换位置
[要求]
期望复杂度为 O ( log n ) O(\log n) O(logn)
输入描述:
第一行两个整数N, num。分别表示数组大小, 需要找的数。
接下来一行N个整数表示数组内的数。
输出描述:
若num存在于数组中,输出"Yes",否则输出"No"
示例1
输入
7 7
4 5 6 7 1 2 3
输出
Yes
示例2
输入
7 998244353
4 5 6 7 1 2 3
输出
No
备注:
1
⩽
N
⩽
1
0
5
1 \leqslant N \leqslant 10^5
1⩽N⩽105
1
⩽
a
r
r
i
⩽
1
0
9
1 \leqslant arr_i \leqslant 10^9
1⩽arri⩽109
解法一(两次二分):
参考 在有序旋转数组中找到最小值 一题,如果我们可以找到分界点,就可以确定 num 在左右哪个区间,然后直接在该区间上进行正常的二分查找即可。
把复杂问题分解成简单的子问题 思想很重要。
解法一代码:
#include <cstdio>
using namespace std;
const int N = 100000;
int n, k;
int a[N];
int main(void) {
scanf("%d%d", &n, &k);
for ( int i = 0; i <n; ++i )
scanf("%d", a + i);
--n;
while ( n > 0 && a[n] == a[0] ) --n;
if ( a[0] == a[n] ) {
puts( a[0] == k ? "Yes" : "No");
return 0;
}
int l = 0, r = n, m;
if ( a[n] < a[0] ) {
while ( l < r ) {
m = l + r >> 1;
if ( a[m] >= a[0] ) l = m + 1;
else r = m;
}
if ( k <= a[n] ) r = n;
else l = 0, --r;
}
while ( l < r ) {
m = l + r >> 1;
if ( a[m] == k ) {
puts("Yes");
return 0;
}
if ( a[m] > k ) r = m;
else l = m + 1;
}
puts( a[r] == k ? "Yes" : "No");
return 0;
}
解法二(一次二分):
还是参考 在有序旋转数组中找到最小值 。我们可以在二分过程中,通过一些条件判断 num 在哪个区间:
- 若
a
[
m
i
d
]
>
=
a
[
0
]
a[mid] >= a[0]
a[mid]>=a[0],说明 mid 在左边区间:
- 若 n u m > = a [ l ] a n d n u m < = a [ m i d ] num >= a[l] \quad and \quad num <= a[mid] num>=a[l]andnum<=a[mid],说明此时要在区间 [l,mid] 查找, r = mid;
- 若 n u m < a [ l ] num < a[l] num<a[l],说明 num 在右边区间,l = mid + 1;
- 若 n u m > a [ m i d ] num > a[mid] num>a[mid],说明 num 在 mid 右边,l = mid + 1;
- 若
a
[
m
i
d
]
<
a
[
0
]
a[mid] < a[0]
a[mid]<a[0],说明 mid 在右边区间:
- 若 n u m < = a [ m i d ] num <= a[mid] num<=a[mid],说明 num 在 mid 左边,r = mid;
- 若 $num > a[n] $,说明 num 在左边区间,r = mid;
- 否则的话,说明 num 在 mid 右边,l = mid + 1;
解法二代码:
#include <cstdio>
using namespace std;
const int N = 100000;
int n, k;
int a[N];
int main(void) {
scanf("%d%d", &n, &k);
for ( int i = 0; i <n; ++i )
scanf("%d", a + i);
--n;
while ( n > 0 && a[n] == a[0] ) --n;
if ( a[0] == a[n] ) {
puts( a[0] == k ? "Yes" : "No");
return 0;
}
int l = 0, r = n, m;
while ( l < r ) {
m = l + r >> 1;
if ( a[m] == k ) {
puts("Yes");
return 0;
}
if ( a[m] >= a[0] ) {
if ( a[l] <= k && k <= a[m] ) r = m;
else l = m + 1;
} else {
if ( k <= a[m] || k > a[n]) r = m;
else l = m + 1;
}
}
puts( a[r] == k ? "Yes" : "No");
return 0;
}