CS-Notes/notes/11. 旋转数组的最小数字.md

73 lines
2.8 KiB
Java
Raw Normal View History

2019-11-02 12:07:41 +08:00
# 11. 旋转数组的最小数字
[NowCoder](https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=13&tqId=11159&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
## 题目描述
把一个数组最开始的若干个元素搬到数组的末尾我们称之为数组的旋转输入一个非递减排序的数组的一个旋转输出旋转数组的最小元素
<div align="center"> <img src="pics/0038204c-4b8a-42a5-921d-080f6674f989.png" width="210px"> </div><br>
## 解题思路
将旋转数组对半分可以得到一个包含最小元素的新旋转数组以及一个非递减排序的数组新的旋转数组的数组元素是原数组的一半从而将问题规模减少了一半这种折半性质的算法的时间复杂度为 O(logN)为了方便这里将 log<sub>2</sub>N 写为 logN
<div align="center"> <img src="pics/424f34ab-a9fd-49a6-9969-d76b42251365.png" width="300px"> </div><br>
此时问题的关键在于确定对半分得到的两个数组哪一个是旋转数组哪一个是非递减数组我们很容易知道非递减数组的第一个元素一定小于等于最后一个元素
通过修改二分查找算法进行求解l 代表 lowm 代表 midh 代表 high
- nums[m] <= nums[h] 表示 [m, h] 区间内的数组是非递减数组[l, m] 区间内的数组是旋转数组此时令 h = m
- 否则 [m + 1, h] 区间内的数组是旋转数组 l = m + 1
```java
public int minNumberInRotateArray(int[] nums) {
if (nums.length == 0)
return 0;
int l = 0, h = nums.length - 1;
while (l < h) {
int m = l + (h - l) / 2;
if (nums[m] <= nums[h])
h = m;
else
l = m + 1;
}
return nums[l];
}
```
如果数组元素允许重复会出现一个特殊的情况nums[l] == nums[m] == nums[h]此时无法确定解在哪个区间需要切换到顺序查找例如对于数组 {1,1,1,0,1}lm h 指向的数都为 1此时无法知道最小数字 0 在哪个区间
```java
public int minNumberInRotateArray(int[] nums) {
if (nums.length == 0)
return 0;
int l = 0, h = nums.length - 1;
while (l < h) {
int m = l + (h - l) / 2;
if (nums[l] == nums[m] && nums[m] == nums[h])
return minNumber(nums, l, h);
else if (nums[m] <= nums[h])
h = m;
else
l = m + 1;
}
return nums[l];
}
private int minNumber(int[] nums, int l, int h) {
for (int i = l; i < h; i++)
if (nums[i] > nums[i + 1])
return nums[i + 1];
return nums[l];
}
```
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-1.png"></img></div>