数据范围:
,数组中任意元素的值:
要求:空间复杂度:
,时间复杂度:
[3,4,5,1,2]
1
[3,100,200,3]
3
public class Solution {
//由于这个旋转数组断裂口的判断依据是 通过一个区间内左边大于右边来判断的断裂,以此获取最小值
//左边的边界点由于需要判断相等的存在,左点需要向右移动,
//这会导致左边点滑落断裂口,由第一个升序区间,滑落到第二个升序区间
//因此,如果要通过左区间来判断最小值,则需要两重判断,左点在第一升序区间的逻辑,左点在第二升序区间的逻辑
//
//如果用右边区间进行判断,则右点始终在第二个升序区间,这只要一套逻辑,因此右区间来判断简洁高效很多
//这里我用的是左区间判断
public int minNumberInRotateArray (int[] nums) {
// write code here
int right = nums.length-1;
int left = 0;
for(int mid = (right+left)/2; ;left<right; mid=(right+left)/2){
//当左点在第一升序区间时
if(nums[left]>=nums[right])
//最小值在left大于mid的区间内
if(nums[left]<nums[mid])
left = mid;
else if(nums[left]>nums[mid])
right = mid;
else
left = left +1;//这里的左点向右爬可能会导致左点掉到第二区间
else//当左点在第二升序区间时,
break;//由于是爬了一步越过断裂口掉下去的,直接left这个点就是最小值
}
return nums[left];
}
} import java.util.*;
public class Solution {
public int minNumberInRotateArray (int[] nums) {
Integer min = Integer.MIN_VALUE;
int start = 0;
int end = nums.length - 1;
return min(nums,start,end);
}
/**
递归+二分法实现,简练
*/
public int min(int[] nums,int start ,int end){
while(start < end){
int mid = (start + end)/2;
if(nums[mid] > nums[end]){
// 中间大于尾部时 比如2,1
start = mid + 1;
}else if(nums[mid] < nums[end]){
//尾部大于中间时。比如1,2
end = mid;
}else{
// 中间等于尾部时,比如1,2,2或者2,1,1
Integer minValue = min(nums,start,mid);
Integer minValue2 = min(nums,mid + 1,end);
return Math.min(minValue,minValue2);
}
}
return nums[start];
}
} public int minNumberInRotateArray (int[] nums) {
int m = minNumberInRotateArray2(nums);
return nums[m];
}
public int minNumberInRotateArray2 (int[] nums) {
int s = 0,e = nums.length-1;
int m = 0;
while(s < e){
m = (s+e) / 2;
if(nums[m] > nums[e]){
s = m+1;
}else if(nums[m] < nums[e]){
e = m;
}else{
e--;
}
}
return s;
}
public int minNumberInRotateArray1 (int[] nums) {
int m = minNumberInRotateArray1_1(nums,0,nums.length-1);
return nums[m];
}
public int minNumberInRotateArray1_1 (int[] nums,int s,int e) {
if(s >= e) return s;
int m = (s+e)/2;
int n1 = minNumberInRotateArray1_1(nums,s,m);
int n2 = minNumberInRotateArray1_1(nums,m+1,e);
if(nums[n1] < nums[n2])
return n1;
else
return n2;
} import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int minNumberInRotateArray (int[] nums) {
// write code here
// 整个问题本是上是在找数组中唯一存在的极小值,采取二分法解决
// 算法的时间复杂度O(logN),额外空间复杂度O(1)
int n = nums.length;
if (n == 1) {
// 如果只有一个元素
return nums[0];
}
if (n == 2) {
// 如果只有两个元素
return Math.min(nums[0], nums[1]);
}
// 确保有三个以上的元素
int l = 0;
int r = n - 1;
// 采用二分思想,任务是找到这样一个转折点[↗……↗(↘index)↗……↗],其特征是左边比它大,右边也比它大
int min = nums[l];
while (l < r) {
// 因为数组非递减,且涉及到右划l
// 所以采用一个变量min时刻与当前最左的l元素比较总不是坏事
min = Math.min(min, nums[l]);
int mid = (l + r) / 2;
if (nums[l] > nums[mid]) {
// 如果mid处比最左处要小,即[↗……↗(↘index)↗……mid……↗],说明局部最小值一定在左半区(或mid本身)
r = mid;
} else if (nums[l] < nums[mid]) {
// 如果mid处比最左处要大,即[↗……mid……↗(↘index)↗……↗],说明局部最小值一定在右半区(必不包括mid)
l = mid + 1;
} else {
// 如果mid处与最左处相等,即[-->……mid……-->(↘index)↗……-->],或[-->……-->(↘index)↗……-->……mid……-->],在左还是在右不能确定
// 但是!至少说明l处一定不是极小值点,此时l往右移一位,缩小范围即可
l++;
}
}
return Math.min(min, nums[l]);
}
} import java.util.*;
class Solution {
public int minNumberInRotateArray (int[] nums) {
int l = 0;
int r = nums.length;
while (l < r) {
int mid = (l + r) / 2;
if (nums[mid] > nums[r - 1]) {
l = mid + 1;
} else if (nums[mid] == nums[r - 1]) {
r--;
} else {
r = mid;
}
}
return nums[l];
}
} 大佬帮忙看看为什么右边取开区间不行, 取闭区间就能过import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int minNumberInRotateArray (int[] nums) {
// write code here
if (nums.length == 0)
return 0;
//假设第一个元素是最小的
int min = nums[0];
for (int i = 1; i < nums.length; i++) {
if (nums[i] < min)
// 如果找到更小的元素,则更新最小值
min = nums[i];
}
return min;
}
} import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int minNumberInRotateArray (int[] nums) {
// 这里可以将数组分解为两部分
// 这两部分均为 从小到大排序的数组,以 12345 为例
// 会有 34512、45123 等
// write code here
// 先来判断一下数组为空的情况
if(nums.length == 0) {
return 0;
}
// 使用二分法来解决
int start = 0;
int end = nums.length - 1;
// 这里是旋转数组,先来考虑第一种情况
// 当这里的第一个元素的值 比 最后一个元素的值小
// 对于上面的例子,可以断定这里的第一个值 就一定是 最小值
if(nums[start] < nums[end]) {
return nums[start];
}
// 之后通过 二分法来判断最小值
// 先来说明没有重复值的情况
// 不断的通过 左、右 侧加逼
// 最终会使得 start 在左侧部分的最大值(最后一个位置) 上
// 并且会使得 end 在右侧部分上达到其中的 最小值(这个就是我们需要的值)
// 此时 start 和 end 就是一个相邻的关系,所以对于 while 设定这样一个判断条件
while(start != end-1) {
int mid = (start + end) / 2;
// 在这之中还需要处理多个重复数据的情况,并且处理三个指针重叠后的情况
// 如题例:1,0,1,1,1
if(nums[start] == nums[end] && nums[start] == nums[mid]) {
// 这里定义一个方法专门来处理这个问题
return minNum(nums,start,end);
}
if(nums[mid] >= nums[start]) {
start = mid;
}else if(nums[mid] <= nums[end]){
end = mid;
}
}
// 在完成上面的判断后,这里的 end 位置的值就是我们要找到的最小值
return nums[end];
}
private int minNum(int[] nums, int start, int end) {
// 这里需要注意的是,要将值设置为 当前重合处的值,不能设置为 0 !!!
int re = nums[start];
for (int i=0; i < nums.length; i++) {
if(re > nums[i]) {
// 将这里的最小值记录下来并返回
re = nums[i];
}
}
return re;
}
} public int minNumberInRotateArray (int[] nums) {
// write code here
if(nums.length <= 0){
return 0;
}
int rt = nums[0];
for(int info : nums){
if(rt > info){
rt = info;
}
}
return rt;
}
不考虑二分是不是更快? import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int minNumberInRotateArray (int[] nums) {
// write code here
return binary(nums, 0, nums.length - 1);
}
public int binary(int[] nums, int left, int right) {
if (left >= right || nums[left] < nums[right]) {
return nums[left];
}
int mid = left + (right - left) / 2;
int leftNum = binary(nums, left, mid);
int rightNum = binary(nums, mid + 1, right);
return leftNum <= rightNum ? leftNum : rightNum;
}
} public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型一维数组
* @return int整型
*/
public int minNumberInRotateArray (int[] nums) {
// write code here
a(nums,0,nums.length-1);
return nums[0];
}
public void a(int[] nums,int left,int right){
if(left>=right)
return;
int mid=(left+right)/2;
a(nums,left,mid);
a(nums,mid+1,right);
b(nums,left,mid,right);
}
public void b(int[] nums,int left,int mid,int right){
if(nums[left]>nums[mid+1]){
nums[left]=nums[mid+1];
}
}
}
剑指Offer中有这道题目的分析。这是一道二分查找的变形的题目。
旋转之后的数组实际上可以划分成两个有序的子数组:前面子数组的大小都大于后面子数组中的元素
注意到实际上最小的元素就是两个子数组的分界线。本题目给出的数组一定程度上是排序的,因此我们试着用二分查找法寻找这个最小的元素。
思路:
(1)我们用两个指针left,right分别指向数组的第一个元素和最后一个元素。按照题目的旋转的规则,第一个元素应该是大于最后一个元素的(没有重复的元素)。
但是如果不是旋转,第一个元素肯定小于最后一个元素。
(2)找到数组的中间元素。
中间元素大于第一个元素,则中间元素位于前面的递增子数组,此时最小元素位于中间元素的后面。我们可以让第一个指针left指向中间元素。
移动之后,第一个指针仍然位于前面的递增数组中。
中间元素小于第一个元素,则中间元素位于后面的递增子数组,此时最小元素位于中间元素的前面。我们可以让第二个指针right指向中间元素。
移动之后,第二个指针仍然位于后面的递增数组中。
这样可以缩小寻找的范围。
(3)按照以上思路,第一个指针left总是指向前面递增数组的元素,第二个指针right总是指向后面递增的数组元素。
最终第一个指针将指向前面数组的最后一个元素,第二个指针指向后面数组中的第一个元素。
也就是说他们将指向两个相邻的元素,而第二个指针指向的刚好是最小的元素,这就是循环的结束条件。
到目前为止以上思路很耗的解决了没有重复数字的情况,这一道题目添加上了这一要求,有了重复数字。
因此这一道题目比上一道题目多了些特殊情况:
我们看一组例子:{1,0,1,1,1} 和 {1,1, 1,0,1} 都可以看成是递增排序数组{0,1,1,1,1}的旋转。
这种情况下我们无法继续用上一道题目的解法,去解决这道题目。因为在这两个数组中,第一个数字,最后一个数字,中间数字都是1。
第一种情况下,中间数字位于后面的子数组,第二种情况,中间数字位于前面的子数组。
因此当两个指针指向的数字和中间数字相同的时候,我们无法确定中间数字1是属于前面的子数组(绿色表示)还是属于后面的子数组(紫色表示)。
也就无法移动指针来缩小查找的范围。
#include <iostream> #include <vector> #include <string> #include <stack> #include <algorithm> using namespace std; class Solution { public: int minNumberInRotateArray(vector<int> rotateArray) { int size = rotateArray.size(); if(size == 0){ return 0; }//if int left = 0,right = size - 1; int mid = 0; // rotateArray[left] >= rotateArray[right] 确保旋转 while(rotateArray[left] >= rotateArray[right]){ // 分界点 if(right - left == 1){ mid = right; break; }//if mid = left + (right - left) / 2; // rotateArray[left] rotateArray[right] rotateArray[mid]三者相等 // 无法确定中间元素是属于前面还是后面的递增子数组 // 只能顺序查找 if(rotateArray[left] == rotateArray[right] && rotateArray[left] == rotateArray[mid]){ return MinOrder(rotateArray,left,right); }//if // 中间元素位于前面的递增子数组 // 此时最小元素位于中间元素的后面 if(rotateArray[mid] >= rotateArray[left]){ left = mid; }//if // 中间元素位于后面的递增子数组 // 此时最小元素位于中间元素的前面 else{ right = mid; }//else }//while return rotateArray[mid]; } private: // 顺序寻找最小值 int MinOrder(vector<int> &num,int left,int right){ int result = num[left]; for(int i = left + 1;i < right;++i){ if(num[i] < result){ result = num[i]; }//if }//for return result; } }; int main(){ Solution s; //vector<int> num = {0,1,2,3,4,5}; //vector<int> num = {4,5,6,7,1,2,3}; vector<int> num = {2,2,2,2,1,2}; int result = s.minNumberInRotateArray(num); // 输出 cout<<result<<endl; return 0; }