ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 1. 寻找旋转排序数组中的最小值 I 假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。 你需要找到其中最小的元素。 你可以假设数组中不存在重复的元素。 样例 > 给出[4,5,6,7,0,1,2] 返回 0 **规则** 输入:int a[] 一个有序数组旋转后的数组 输出:int 最小的值 case: ``` 数组为null 数组大小为0 数组旋转不变 [0,1,2,3,4,5,6] 数组旋转 [6,0,1,2,3,4,5] 数组旋转 [1,2,3,4,5,6,0] 数组旋转 [2,3,4,5,6,0,1] 数组旋转 [4,5,6,0,1,2,3] ``` **思路** 最简单的思路当然是遍历一下,复杂度为O(n)和O(1) 也可以使用一次堆排序,复杂度为O(logn)和O(1) 由于是部分有序的,可以考虑使用二分法,复杂度为O(logn)和O(1) [0,1,2,3,4,5,6] 旋转后的序列在[0,1,2,3,4,5,6,0,1,2,3,4,5,6] 从里面取相应的长度的数即可,从里面可以看到,最小值0的位置,旋转初期在右侧,最后在左侧,可以选取mid与begin和end对比,例如 ``` [1,2,3,4,5,6,0] begin=1 mid=4 end=0 当mid>end时说明最小值在右侧 [5,6,0,1,2,3,4] begin=5 mid=1 end=4 当mid<begin时说明最小值在左侧 ``` 这样,我们可以使用二分法进行对比,需要注意的是:begin、mid和end的值如何设置,特别是边界条件[0] [0,1]这种少于三个数据的情况,还有mid=min的情况。 我们可以设置只有两个的时候就停止,因为最小数据必然在这两个里面。而大于两个时,如[2,0,1],mid<begin,在左侧,这时应该从区间[0,2] -> [begin,mid] = [2,0] 在这里面寻找。这时因为,未旋转的数组是有序的,单调递增,而旋转过后,只有不与原来的数组相同,那么一定是递减的。因此如果出现单调递减的,就说明最小值在这个范围内。 **代码** ``` public class Solution { /* * @param nums: a rotated sorted array * @return: the minimum number in the array */ public int findMin(int a[]) { if(a == null || a.length == 0) return -1; // 异常条件 int len = a.length; if(a[0] < a[len-1]) { // 特殊情况,相当于没有旋转 return a[0]; } return search(a,0,len-1); // 初始范围 } private int search(int a[],int begin,int end) { if(begin == end) return a[begin]; // 如果只有一个数 if(begin+1 == end) return Math.min(a[begin], a[end]); // 如果只有两个数 int mid = begin + (end - begin) / 2; // 计算mid if(a[begin] > a[mid]) { // 左边 return search(a,begin,mid); }else if(a[mid] > a[end]) { // 右边 return search(a,mid,end); } return -1; } } ``` 上面的实现使用了递归,势必造成需要一定的栈空间,可以改造成循环 ``` public class Solution { /* * @param nums: a rotated sorted array * @return: the minimum number in the array */ public int findMin(int a[]) { if(a == null || a.length == 0) return -1; int len = a.length; if(a[0] < a[len-1]) { return a[0]; } int begin = 0 , end = a.length - 1; while(true) { // 循环 if(begin == end) return a[begin]; if(begin+1 == end) return Math.min(a[begin], a[end]); int mid = begin + (end - begin) / 2; if(a[begin] > a[mid]) { end = mid; }else if(a[mid] > a[end]) { begin = mid; } } } } ``` 使用一次堆排序的程序 ``` package com.shisj.study.offer.chapter1.revertnum; /** * 使用堆排序 * @author shisj * */ public class SearchSmallest0 { public int findMin(int a[]) { if(a == null || a.length == 0) return -1; int len = a.length; int begin = (len-2)/2; while(begin >=0) { swap(a,begin,begin*2+1); swap(a,begin,begin*2+2); begin --; } return a[0]; } private void swap(int a[],int parent,int child) { if(child > a.length-1)return; if(a[parent] > a[child]) { int tmp = a[child]; a[child] = a[parent]; a[parent] = tmp; } } } ``` ## 2. 寻找旋转排序数组中的最小值 II 假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。 你需要找到其中最小的元素。 数组中可能存在重复的元素。 样例: 给出[4,4,5,6,7,0,1,2] 返回 0 **规则** 输入:int a[] 一个有序数组旋转后的数组 输出:int 最小的值 case: ``` 数组为null 数组大小为0 数组旋转不变 [0,0,0,0,1,1,2,3,4,6] 数组旋转 [0,0,1,1,2,3,4,6,0,0] 数组旋转 [1,1,2,3,4,6,0,0,0,0] 数组旋转 [3,4,6,0,0,0,0,1,1,2] ``` **思路** 这次加入了重复元素,首先要判断是否可以使用二分法,通过分析,在上一个题目的基础上增加了相等的情形,如[1,1,1,-1,1] 这次,mid=1 begin=1 end=1 只能两个都查找,然后比较大小了。 使用一次堆排序的话不收影响。 **规则** ``` public class Solution { /* * @param nums: a rotated sorted array * @return: the minimum number in the array */ public int findMin(int a[]) { if(a == null || a.length == 0) return -1; int len = a.length; if(a[0] < a[len-1]) { return a[0]; } return search(a,0,len-1); } private int search(int a[],int begin,int end) { if(begin == end) return a[begin]; if(begin+1 == end) return Math.min(a[begin], a[end]); int mid = begin + (end - begin) / 2; if(a[begin] > a[mid]) { return search(a,begin,mid); }else if(a[mid] > a[end]) { return search(a,mid,end); }else{ int ax = search(a,begin,mid); int bx = search(a,mid+1,end); return Math.min(ax,bx); } } } ```