前言
当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~
在此感谢左大神让我对算法有了新的感悟认识!
问题介绍
原问题
给定一个有序旋转数组,如[1,2,3,4,5,6,7]旋转后为[3,4,5,6,7,1,2],在给定一个数num,以最快的速度判断num是否在数组中。
解决方案
原问题:
上一期中讲到求旋转数组中最小的值,这里求指定的值,思路大体一致
1、首先判断是否l、r、mid都相等,如果三个地方的值都相等,则无法判断出num是否在arr中,此时left单步前进直到left超过mid+1,进入下一轮循环或者arr[left]!=arr[mid]为止
2、其次如果三个值不都相等时,首先排除掉两端不相等的区间,再次排除掉存在断点的区间,最后排除掉确定不存在num的区间。
详情看代码和最终感悟。
代码编写
java语言版本
原问题:
方法一:
/**
* 二轮测试:判断num是否在旋转数组arr中
* @param arr
* @param num
* @return
*/
public static boolean findNumCp1(int[] arr, int num) {
if (arr == null || arr.length == 0) {
return false;
}
int left = 0;
int right = arr.length-1;
while (left < right) {
int mid = (left + right) / 2;
if (arr[mid] == num) {
return true;
}
if (arr[mid] == arr[left] && arr[left] == arr[right]) {
// 三个地方都相等,需要通过遍历打破这个局面
while (arr[left] == arr[mid] && left < mid) {
left++;
}
if (left == mid) {
// 防止越界
left = mid+1;
continue;
}
}
if (arr[left] != arr[mid]) {
if (arr[left] < arr[mid]) {
if (num >= arr[left] && num < arr[mid]) {
right = mid-1;
}else {
left = mid + 1;
}
}else {
if (num > arr[mid] && num <= arr[right]) {
left = mid + 1;
}else {
right = mid - 1;
}
}
}else {
if (arr[mid] < arr[right]) {
if (num > arr[mid] && num < arr[right]) {
left = mid + 1;
}else {
right = mid - 1;
}
}else {
if (num > arr[left] && num < arr[mid]) {
right = mid - 1;
}else {
left = mid + 1;
}
}
}
}
return false;
}
public static void main(String[] args) {
System.out.println(findNumCp1(new int[]{2,3,4,5,1}, 0));
}
c语言版本
正在学习中
c++语言版本
正在学习中
思考感悟
这道题其实用了排除法,我觉得他一直通过if (arr[left] < arr[mid]) { 这种判断是为了判断这个区间是否是单调递增的,如果arr[left] < arr[mid]说明left到mid不存在旋转点,还记得旋转数组的最重要的规律吧,如果arr[0] < arr[len]那么断点一定在arr[0],否则一定不成立,那么这个算法的精髓我觉得就是利用了这个比较的办法首先排除掉存在断点的区间,然后再次使用了if (num > arr[left] && num < arr[mid]) {这种方式排除掉num是否在连续区间,如果不在那么就应该在存在断点的那个区间内。
这道题不仅仅是一道简单的二分法题目,其中利用的思想确实值得借鉴,如果我们解决这类问题时,不一定获取要一下获取到结果,可以将能够判断的区间逐渐的缩小直到最后能够获取到结果为止,排除法值得借鉴
写在最后
方案和代码仅提供学习和思考使用,切勿随意滥用!如有错误和不合理的地方,务必批评指正~
如果需要git源码可邮件给2260755767@qq.com
再次感谢左大神对我算法的指点迷津!