面试题8:旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。

在数组{3,4,5,1,2}中查找最小值的过程:

#include <iostream>
using namespace std;int Min(int *numbers, int length)
{if (numbers == nullptr || length <= 0) throw new exception("Invalid parameters");int idx1 = 0;int idx2 = length - 1;int idxmin = idx1;while (numbers[idx1] >= numbers[idx2]){if (idx2 - idx1 == 1){idxmin = idx2;break;}idxmin = (idx1 + idx2) / 2;if (numbers[idxmin] >= numbers[idx1]) idx1 = idxmin;else if (numbers[idxmin] <= numbers[idx2]) idx2 = idxmin;}return numbers[idxmin];
}

前面我们提到在旋转数组中,由于是把递增排序数组前面的若干个数字搬到数组的后面,因此第一个数字总是大于或者等于最后一个数字。
但按照定义还有一个特例:如果把排序数组的前面的 0 个元素搬到最后面,即排序数组本身,这仍然是数组的一个旋转,我们的代码需要支持这种情况。
此时,数组中的第一个数字就是最小的数字,可以直接返回。
这就是在上面的代码中,把indexMid初始化为index1的原因。
一旦发现数组中第一个数字小于最后一个数字,表明该数组是排序的,就可以直接返回第一个数字了。

上述代码是否就完美了呢?面试官会告诉我们其实不然。
他将提示我们再仔细分析下标为index1和index2(index1和index2分别和图中P1和P2相对应)的两个数相同的情况。

在前面的代码中,当这两个数相同,并且它们中间的数字(即indexMid指向的数字)也相同时,我们把indexMid赋值给了 index1,也就是认为此时最小的数字位于中间数字的后面。

是不是一定这样?

我们再来看一个例子。

数组{1,0,1,1,1}和数组{1,1,1,0,1}都可以看成是递增排序数组{0,1,1,1,1}的旋转,图2.11分别画出它们由最小数字分隔开的两个子数组。


图2.11 数组{0,1,1,1,1}的两个旋转{1,0,1,1,1}和{1,1,1,0,1}

注:在这两个数组中,第一个数字、最后一个数字和中间数字都是1,我们无法确定中间的数字 1 属于第一个递增子数组还是属于第二个递增子数组。
第二个子数组用灰色背景表示。

在这两种情况中,第一个指针和第二个指针指向的数字都是1,并且两个指针中间的数字也是1,这3个数字相同。
在第一种情况中,中间数字(下标为2)位于后面的子数组;
在第二种情况中,中间数字(下标为2)位于前面的子数组中。

因此,当两个指针指向的数字及它们中间的数字三者相同的时候,我们无法判断中间的数字是位于前面的子数组中还是后面的子数组中,也就无法移动两个指针来缩小查找的范围。

此时,我们不得不采用顺序查找的方法。

在把问题分析清楚形成清晰的思路之后,我们就可以把前面的代码修改为:

#include <iostream>
using namespace std;int Min(int *numbers, int length)
{if (numbers == nullptr || length <= 0) throw new exception("Invalid parameters");int idx1 = 0;int idx2 = length - 1;int idxmin = idx1;while (numbers[idx1] >= numbers[idx2]){if (idx2 - idx1 == 1){idxmin = idx2;break;}idxmin = (idx1 + idx2) / 2;if (numbers[idx1] == numbers[idx2] && numbers[idxmin] == numbers[idx1]) return MinInOrder(numbers, idx1, idx2);if (numbers[idxmin] >= numbers[idx1]) idx1 = idxmin;else if (numbers[idxmin] <= numbers[idx2]) idx2 = idxmin;}return numbers[idxmin];
}int MinInOrder(int *numbers, int idx1, int idx2)
{int res = numbers[idx1];for (int i = idx1 + 1; i <= idx2; i++){if (res > numbers[i]) res = numbers[i];}return res;
}

测试用例:
● 功能测试(输入的数组是升序排序数组的一个旋转,数组中有重复数字或者没有重复数字)。
● 边界值测试(输入的数组是一个升序排序的数组、只包含一个数字的数组)。
● 特殊输入测试(输入NULL指针)。
本题考点:
● 考查对二分查找的理解。本题变换了二分查找的条件,输入的数组不是排序的,而是排序数组的一个旋转。这要求我们对二分查找的过程有深刻的理解。
● 考查沟通学习能力。本题面试官提出了一个新的概念:数组的旋转。我们要在很短时间内学习理解这个新概念。在面试过程中如果面试官提出新的概念,我们可以主动和面试官沟通,多问几个问题把概念弄清楚。
● 考查思维的全面性。排序数组本身是数组旋转的一个特例。另外,我们要考虑到数组中有相同数字的特例。如果不能很好地处理这些特例,就很难写出让面试官满意的完美代码。

[剑指offer]面试题8:旋转数组的最小数字相关推荐

  1. 剑指offer面试题[8]-旋转数组的最小数字

    题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个 ...

  2. 剑指Offer - 面试题11. 旋转数组的最小数字(二分查找)

    1. 题目 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] ...

  3. 剑指offer面试题11. 旋转数组的最小数字(二分查找)

    题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素.例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的 ...

  4. 剑指offer(12)旋转数组的最小数字

    把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组 ...

  5. 剑指offe面试题8 旋转数组的最小数字 (java实现)

    解题思路: 针对旋转数组的特点,即旋转后,数组的前半部分是有序的,后半部分是有序的. 1.先考虑一般情况:可以参考二分查找的思想,在数组中设置两个指针,一个指向数组的起始位置,一个指向数组的结束位置. ...

  6. j剑指offer面试题[33]-把数组排成最小的数

    题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323. 思路: 可以 ...

  7. 剑指Offer - 面试题45. 把数组排成最小的数(字符串排序)

    1. 题目 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个. 示例 1: 输入: [10,2] 输出: "102"示例 2: 输入: [ ...

  8. 剑指Offer:面试题33——把数组排成最小的数(java实现)(未完待续)

    问题描述: 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323. 思路1: ...

  9. 【剑指offer-Java版】08旋转数组的最小数字

    旋转数组最小数字:暴力方法就是遍历-有效的方法是二分,但是存在失效的情况 几个特别的测试用例需要注意下: 数组中含有多个相同的数字 最小数字就是第一个 public class _Q08 {publi ...

  10. 剑指offer——面试题51:数组中重复的数字

    剑指offer--面试题51:数组中重复的数字 Solution1: 20180910更新.利用数组做一次hash映射,时间复杂度为O(n)O(n)O(n),空间复杂度O(n)O(n)O(n). cl ...

最新文章

  1. 个人贷款的条件、流程和注意事项
  2. ​【Python入门】Python数学math模块55个函数详解
  3. 斐波那契数列的前n项值
  4. Go操作mysql实现增删改查及连接池
  5. html5中的一些标签学习总结
  6. Sightseeing Cows POJ - 3621
  7. MyBatis参数为Integer型并赋值为0时判断失误的问题解决
  8. c++11 多线程编程(一)------初始
  9. 人工智能白皮书 附下载地址
  10. Java解析HTML之NekoHTML
  11. win32实现两个透明窗口联动
  12. 【SSD目标检测】3:训练自己的数据集
  13. p-mos,n-mos的导通条件
  14. 使用n2disk和PF_RING构建一个(便宜的)2×10 Gbit(连续)数据包记录器
  15. java怎么取map中的key值,Java获取map中key和value的方法
  16. Java: Unresolved compilation problem的解决方法
  17. 【杂记】收藏的喜欢的句子
  18. 绿地五里桥 设计原型_绿地 · 黄浦滨江项目设计
  19. 计算机组成原理第三章例题解析(关于存储器容量扩充)
  20. Jena对本体、RDF三元组的API操作记录

热门文章

  1. 【ArcGIS遇上Python】ArcGIS python计算长时间序列多个栅格数据的平均值
  2. 【ArcGIS风暴】ArcGIS获取线段上等间距的点
  3. C语言试题二十二之定义了3×3的二维数组,并在主函数中赋值。函数的功能使求出数组周边元素的平均值并作为函数值返回给主函数中的s。
  4. Android开发之运行客户的Demo拿不到数据
  5. linux网络编程之用select函数实现io复用(基于TCP)引发的思考
  6. 用计算机算出鞋子的鞋码,【鞋子尺码对照】鞋子尺码怎么算_鞋子尺码怎么量 - 妈妈网百科...
  7. 你为什么不爱发朋友圈了?
  8. 北方人的快乐。。。| 今日最佳
  9. qt4.7 mysql_详解Qt 4.7编译和访问Mysql驱动
  10. python循环语句嵌套_Python 循环语句