旋转数组中的最小值
来自《剑指Offer》中的一道面试题:
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减的排序的数组的一个旋转,输出旋转数组的最小元素。例如数组 {3, 4, 5, 1, 2} 为 {1, 2, 3, 4, 5} 的一个旋转, 该数组的最小元素为 1。
理清了算法思路后写出了代码,后面看了书上的解法,觉得有些不直观,分享下自己的代码;)
Naive解法
在 O(n)
时间范围内遍历旋转后的数组,可以找到最小的元素。但是,这种解法没有利用旋转数组的特点,即数组内部元素间部分有序,因此,查找效率不高。
改进的解法
旋转数组是对非递增的排序的数组进行旋转的结果,因此,数组内部元素被分为了2个区间,在各区间内部是有序的,例如 {3, 4, 5, 1, 2} ,被分为了 {3, 4, 5 } 和 { 1, 2} 两个有序的子区间,对于有序的数组的查找问题,可以考虑二分查找算法,时间复杂度 O(log(n))
。
那么,如何对旋转的数组采用二分查找法寻找最小的元素呢,基本思路如下:
- 对旋转数组二分, 必然有一个区间内的元素间是严格的非递增有序的;
- 比较中间的元素与二分后两个区间的元素,可以筛选掉一个区间的元素;
- 再二分,再重复第二步骤,直到中间的元素都不大于二分后两个区间内的元素。
呃,感觉还是confusing,上菜:
1 | int findMin(int numbers[], int size){ |
测试数据:
1 | dataset1: {4, 5, 1, 2, 3} |
总结
二分查找算法在面试题中经常遇到,算法很简单,但非常重要,面试题经常会以各种变化的形式[^1]考察。当看到对有序或部分有序的数组进行查找时,可以考虑用二分查找算法。
[^1]: 《剑指Offer》中另一道面试题:统计一个数字在排序数组中出现的次数。例如输入排序数组 {1, 2, 3, 3, 3, 3, 4, 5}, 由于 3 在这个数组中出现了 4 次,因此输出 4。