分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请轻击人工智能教程大家好!欢迎来到我的网站! 人工智能被认为是一种拯救世界、终结世界的技术。毋庸置疑,人工智能时代就要来临了,科… 继续阅读 前言https://www.captainai.net/troubleshooter
package live.every.day.ProgrammingDesign.CodingInterviewGuide.Other;
/**
* 在有序旋转数组中找到一个数
*
* 【题目】
* 有序数组arr可能经过一次旋转处理,也可能没有,且arr可能存在重复的数。例如,有序数组[1,2,3,4,5,6,7],可以旋转处理成
* [4,5,6,7,1,2,3]等。给定一个可能旋转过的有序数组arr,再给定一个数num,返回arr中是否含有num。
*
* 【难度】
* 中等
*
* 【解答】
* 为了方便描述,我们把没经过旋转前,有序数组arr最左边的数,在经过旋转之后所处的位置叫作"断点"。例如,题目例子里的数组,
* 旋转后断点在1所处的位置,也就是位置4。如果一个数组没有经过旋转处理,断点在位置0。
* 本文提供的方式做到了尽可能多地利用二分查找,但是最差情况下仍无法避免O(N)的时间复杂度,以下是具体过程:
* 1.用low和high变量表示arr上的一个范围,每次判断num是否在arr[low..high]上,初始时,low=0,high=arr.length-1,
* 然后进入步骤2。
* 2.如果low>high,直接进入步骤5,否则令变量mid=(low+high)/2,也就是二分的位置。如果arr[mid]==num,直接返回true,
* 否则进入步骤3。
* 3.此时arr[mid]!=num。如果发现arr[low]、arr[mid]、arr[high]三个值不都相等,直接进入步骤4。如果发现三个值都相等,
* 此时根本无法知道断点的位置在mid的哪一侧。例如:7(low)...7(mid)...7(high),举一个极端的例子,如果这个数组中只有一
* 个值为num的数,其他的数都是7,那么num除了不在low、mid、high这三个位置以外,剩下的位置都是可能的。所以num也既可能在
* mid的左边,也可能在右边。所以进行这样的处理:
* 1)只要arr[low]等于arr[mid],就让low不断地向右移动(low++),如果在low移到mid的期间,都没有发现arr[low]和
* arr[mid]不等的情况,说明num只可能在mid的右侧,因为左侧全都扫过了,此时令low=mid+1,high不变,进入步骤2。
* 2)只要arr[low]等于arr[mid],就让low不断地向右移动(low++),如果期间一旦发现arr[low]和arr[mid]不等,说明在此时
* 的arr[low(递增后的)..mid..right]上是可以判断出断点位置的,则进入步骤4。
* 4.此时arr[mid]!=num,并且arr[low]、arr[mid]、arr[high]三个值不都相等,那么是一定可以二分的,具体判断如下:
* 如果arr[low]!=arr[mid],如何判断断点位置呢?分以下两种情况。
* 情况一:arr[mid]>arr[low],断点一定在mid的右侧,此时arr[low..mid]上有序。
* 1)如果num>=arr[low]&&num<arr[mid],说明num只需要在arr[low..mid]上寻找。这是因为如果
* num==arr[low]&&num<arr[mid]。很显然,在arr[low..mid]上能找到num。如果num>arr[low]&&num<arr[mid],则说明
* 断点在右侧,假设断点在mid和high之间的break位置上,那么arr[mid..break-1]上的值都大于或等于arr[mid],也都大于num,
* arr[break..high]上的值都小于或等于arr[low],也都小于num,所以整个mid的右侧都没有num。综上所述,num只需要在
* arr[low..mid]上寻找,令high=mid-1,进入步骤2。
* 2)若不满足条件1),说明要么num<arr[low],此时整个arr[low..mid]上都大于num。要么num>arr[mid],此时整个
* arr[low..mid]上都小于num。无论是哪种,num都只可能出现在mid的右侧,所以令low=mid+1,进入步骤2。
* 情況二:不满足情况一则断点一定在mid位置或在mid左侧,不管是哪一种,arr[mid..high]都一定是有序的。
* 1)如果num>arr[mid]&&num<=arr[high]与情况一的条件1)相同的分析方式,令low=mid+1,进入步骤2。
* 2)若不满足条件1),与情况一的条件2)相同的分析方式,令high=mid-1,进入步骤2。
* 如果arr[mid]!=arr[high],如何判断断点的位置呢?和arr[low]!=arr[mid]时一样的分析方式,这里不再详述。
* 5.如果low在high的右边(low>high),说明arr中没有num,返回false。
* 全部的过程请参看如下代码中的isContains方法。
*
* @author Created by LiveEveryDay
*/
public class InOrderedRotatedArrayFindNum {
public static boolean isContains(int[] arr, int num) {
int low = 0;
int high = arr.length - 1;
int mid = 0;
while (low <= high) {
mid = (low + high) >> 1;
if (arr[mid] == num) {
return true;
}
if (arr[low] == arr[mid] && arr[mid] == arr[high]) {
while (low != mid && arr[low] == arr[mid]) {
low++;
}
if (low == mid) {
low = mid + 1;
continue;
}
}
if (arr[low] != arr[mid]) {
if (arr[mid] > arr[low]) {
if (num >= arr[low] && num < arr[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
} else {
if (num > arr[mid] && num <= arr[high]) {
low = mid + 1;
} else {
high = mid - 1;
}
}
} else {
if (arr[mid] < arr[high]) {
if (num > arr[mid] && num <= arr[high]) {
low = mid + 1;
} else {
high = mid - 1;
}
} else {
if (num >= arr[low] && num < arr[mid]) {
high = mid - 1;
} else {
low = mid + 1;
}
}
}
}
return false;
}
public static void main(String[] args) {
int[] arr = {4, 5, 6, 7, 1, 2, 3};
int num = 6;
System.out.printf("Is contains num? %s", isContains(arr, num));
}
}
// ------ Output ------
/*
Is contains num? true
*/