剑指Offer面试题:7.旋转数组的最小数字
一、题目:旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
这道题最直观的解法并不难,从头到尾遍历数组一次,我们就能找出最小的元素。这种思路的时间复杂度显然是O(n)。但是这个思路没有利用输入的旋转数组的特性,肯定达不到面试官的要求。
我们注意到旋转之后的数组实际上可以划分为两个排序的子数组,而且前面的子数组的元素都大于或者等于后面子数组的元素。我们还注意到最小的元素刚好是这两个子数组的分界线。在排序的数组中我们可以用二分查找法实现O(logn)的查找。
二、解题思路
Step1.和二分查找法一样,我们用两个指针分别指向数组的第一个元素和最后一个元素。
Step2.接着我们可以找到数组中间的元素:
如果该中间元素位于前面的递增子数组,那么它应该大于或者等于第一个指针指向的元素。此时数组中最小的元素应该位于该中间元素的后面。我们可以把第一个指针指向该中间元素,这样可以缩小寻找的范围。移动之后的第一个指针仍然位于前面的递增子数组之中。如果中间元素位于后面的递增子数组,那么它应该小于或者等于第二个指针指向的元素。此时该数组中最小的元素应该位于该中间元素的前面。
Step3.接下来我们再用更新之后的两个指针,重复做新一轮的查找。
按照上述的思路,第一个指针总是指向前面递增数组的元素,而第二个指针总是指向后面递增数组的元素。最终第一个指针将指向前面子数组的最后一个元素,而第二个指针会指向后面子数组的第一个元素。也就是它们最终会指向两个相邻的元素,而第二个指针指向的刚好是最小的元素。这就是循环结束的条件。
以前面的数组{3,4,5,1,2}为例,下图展示了在该数组中查找最小值的过程:

三、解决问题
3.1 代码实现
public static int GetMin(int[] numbers)
{
if (numbers == null || numbers.Length <= )
{
return int.MinValue;
} int index1 = ;
int index2 = numbers.Length - ;
// 把indexMid初始化为index1的原因:
// 一旦发现数组中第一个数字小于最后一个数字,表明该数组是排序的
// 就可以直接返回第一个数字了
int indexMid = index1; while (numbers[index1] >= numbers[index2])
{
// 如果index1和index2指向相邻的两个数,
// 则index1指向第一个递增子数组的最后一个数字,
// index2指向第二个子数组的第一个数字,也就是数组中的最小数字
if (index2 - index1 == )
{
indexMid = index2;
break;
}
indexMid = (index1 + index2) / ;
// 特殊情况:如果下标为index1、index2和indexMid指向的三个数字相等,则只能顺序查找
if (numbers[index1] == numbers[indexMid] && numbers[indexMid] == numbers[index2])
{
return GetMinInOrder(numbers, index1, index2);
}
// 缩小查找范围
if (numbers[indexMid] >= numbers[index1])
{
index1 = indexMid;
}
else if (numbers[indexMid] <= numbers[index2])
{
index2 = indexMid;
}
} return numbers[indexMid];
} public static int GetMinInOrder(int[] numbers, int index1, int index2)
{
int result = numbers[index1];
for (int i = index1 + ; i <= index2; ++i)
{
if (result > numbers[i])
{
result = numbers[i];
}
} return result;
}
这里需要注意的是:
(1)把indexMid初始化为index1的原因:一旦发现数组中第一个数字小于最后一个数字,表明该数组是排序的,就可以直接返回第一个数字了。
(2)特殊情况的分析:如果下标为index1、index2和indexMid指向的三个数字相等,则只能顺序查找,因此这里定义了一个GetMinInOrder()方法。
3.2 单元测试
(1)典型输入,单调升序的数组的一个旋转
// 典型输入,单调升序的数组的一个旋转
[TestMethod]
public void GetMinNumTest1()
{
int[] array = {, , , , };
Assert.AreEqual(Program.GetMin(array),);
}
(2)有重复数字,并且重复的数字刚好的最小的数字
// 有重复数字,并且重复的数字刚好的最小的数字
[TestMethod]
public void GetMinNumTest2()
{
int[] array = { , , , , , };
Assert.AreEqual(Program.GetMin(array), );
}
(3)有重复数字,但重复的数字不是第一个数字和最后一个数字
// 有重复数字,但重复的数字不是第一个数字和最后一个数字
[TestMethod]
public void GetMinNumTest3()
{
int[] array = { , , , , , };
Assert.AreEqual(Program.GetMin(array), );
}
(4)有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字
// 有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字
[TestMethod]
public void GetMinNumTest4()
{
int[] array = { , , , , };
Assert.AreEqual(Program.GetMin(array), );
}
(5)单调升序数组,旋转0个元素,也就是单调升序数组本身
// 单调升序数组,旋转0个元素,也就是单调升序数组本身
[TestMethod]
public void GetMinNumTest5()
{
int[] array = { , , , , };
Assert.AreEqual(Program.GetMin(array), );
}
(6)数组中只有一个数字
// 数组中只有一个数字
[TestMethod]
public void GetMinNumTest6()
{
int[] array = { };
Assert.AreEqual(Program.GetMin(array), );
}
(7)鲁棒性测试:输入NULL
// 鲁棒性测试:输入NULL
[TestMethod]
public void GetMinNumTest7()
{
Assert.AreEqual(Program.GetMin(null), int.MinValue);
}
单元测试的结果如下图所示:

对于GetMin方法编写的单元测试的代码覆盖率已达到了100%:

剑指Offer面试题:7.旋转数组的最小数字的更多相关文章
- 剑指Offer - 九度1386 - 旋转数组的最小数字
剑指Offer - 九度1386 - 旋转数组的最小数字2013-11-24 01:57 题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转 ...
- 剑指offer【06】- 旋转数组的最小数字(java)
题目:旋转数组的最小数字 考点:查找和排序 题目描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4, ...
- 《剑指Offer》算法题——“旋转数组”的最小数字
题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个非递减序列的一个旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数 ...
- 剑指offer六之求旋转数组的最小数字
一.题目 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个 ...
- 【剑指Offer】6、旋转数组的最小数字
题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5 ...
- 剑指Offer:面试题8——旋转数组的最小值(java实现)
题目描述: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入 一个递增排序的数组的一个旋转 输出 旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的 ...
- 剑指Offer:面试题33——把数组排成最小的数(java实现)(未完待续)
问题描述: 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323. 思路1: ...
- 【剑指offer】面试题 11. 旋转数组的最小数字
面试题 11. 旋转数组的最小数字 题目描述 题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4, ...
- Leetcode - 剑指offer 面试题29:数组中出现次数超过一半的数字及其变形(腾讯2015秋招 编程题4)
剑指offer 面试题29:数组中出现次数超过一半的数字 提交网址: http://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163 ...
- 剑指Offer - 九度1504 - 把数组排成最小的数
剑指Offer - 九度1504 - 把数组排成最小的数2014-02-06 00:19 题目描述: 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输 ...
随机推荐
- Codeforces #380 Subordinates(贪心 构造)
从前往后扫,找到一出现次数为0的数,从后面找一个出现不为0的数转化而来.设置两指针l, r来处理. #include<cstdio> #include<iostream> #i ...
- Xamarin教程索引页
持续更新中-- 近期学习处理Xamarin.Android动画内容 Xamarin指南 -- 官网教程翻译 Xamarin跨平台开发 Xamarin Workbooks Xamarin Workboo ...
- Linux学习笔记(11)-kill函数
明天开始学习kill函数的用法. ---------------------------------------------- kill函数可以用来向指定的进程发送一个指定的信号,在我的理解的来看,就 ...
- Redis——学习之路四(初识主从配置)
首先我们配置一台master服务器,两台slave服务器.master服务器配置就是默认配置 端口为6379,添加就一个密码CeshiPassword,然后启动master服务器. 两台slave服务 ...
- LeetCode 258. Add Digits
Problem: Given a non-negative integer num, repeatedly add all its digits until the result has only o ...
- Ancient Printer[HDU3460]
Ancient Printer Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others)Tot ...
- tornado学习笔记18 _RequestDispatcher 请求分发器
根据Application的配置,主要负责将客户端的请求分发到具体的RequestHandler.这个类实现了HTTPMessageDelegate接口. 18.1 构造函数 定义: def __in ...
- mac 终端常用命令
1.复制文件内容到剪贴板:pbcopy < ~/.ssh/id_rsa.pub. 2.ssh key 的生成,参考mac ssh key 的获取. 3.sourcetree 需要输入的密码,指的 ...
- I/O Techie 社区 --欢迎您的加入
I/O Techie 社区 上线了,希望能聚集更多的软件开发者,提供给处于各个阶段的新鸟,老鸟更多的帮助和更好的服务. 链接:http://www.iotechie.info/ Google +:ht ...
- Flex DataGrid可编辑对象实现Enter跳转
来源:http://blog.sina.com.cn/s/blog_5ed17a730100vrja.html 在Flex DataGrid中实现点击Enter键可编辑对象跳转<?xml ver ...