面试题8:旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1.
预备知识:
排序: 归并排序,快速排序,插入排序,冒泡排序
查找:二分查找,顺序查找,哈希表查找,二叉树排序树查找
指标:额外空间消耗,平均时间复杂度和最差时间复杂度
实现方式:递归和循环两种方式
位运算:与&,或|,异或^,左移<<,右移>>
思路:首先要弄清楚题目的意思以及相关的概念,接着需要自己通过一个简单的例子进行具体分析如何查找,最后寻找特殊的例子,如边界值或特殊输入进行分析即可,这里需要注意临界条件。
算法实现:
#include "stdafx.h"
#include<exception> //C++异常处理
int MinInOrder(int* numbers, int index1, int index2);
int Min(int* numbers, int length)
{
if(numbers == NULL || length <= 0)
throw new std::exception("Invalid parameters"); //异常处理类class exception;
//在C/C++数组索引是从0开始的
int index1 = 0; //开头元素索引
int index2 = length - 1; //结束元素索引
int indexMid = index1; //当前面0个元素搬到最后面,即保存不变的时候
while(numbers[index1] >= numbers[index2]) //临界条件
{
// 如果index1和index2指向相邻的两个数,
// 则index1指向第一个递增子数组的最后一个数字,
// index2指向第二个子数组的第一个数字,也就是数组中的最小数字
if(index2 - index1 == 1) //相邻元素
{
indexMid = index2;
break;
}
// 如果下标为index1、index2和indexMid指向的三个数字相等,
// 则只能顺序查找
indexMid = (index1 + index2) / 2;
if(numbers[index1] == numbers[index2] && numbers[indexMid] == numbers[index1]) //特例的情况
return MinInOrder(numbers, index1, index2); //顺序查找
// 缩小查找范围
if(numbers[indexMid] >= numbers[index1]) //中间元素大于等于前面的元素,说明这个元素位于第一递增数组中
index1 = indexMid;
else if(numbers[indexMid] <= numbers[index2])//中间元素小于等于后面的元素,说明这个元素位于第二递增数组中
index2 = indexMid;
}
return numbers[indexMid];
}
//顺序查找最小值
int MinInOrder(int* numbers, int index1, int index2)
{
int result = numbers[index1];
for(int i = index1 + 1; i <= index2; ++i) //在数组中遍历每一个值
{
if(result > numbers[i])
result = numbers[i];
}
return result;
}
// ====================测试代码====================
void Test(int* numbers, int length, int expected)
{
int result = 0;
try
{
result = Min(numbers, length); //二分查找最小
for(int i = 0; i < length; ++i)
printf("%d ", numbers[i]);
if(result == expected)
printf("\tpassed\n");
else
printf("\tfailed\n");
}
catch (...)
{
if(numbers == NULL) //特殊输入测试
printf("Test passed.\n");
else
printf("Test failed.\n");
}
}
int _tmain(int argc, _TCHAR* argv[])
{
// 典型输入,单调升序的数组的一个旋转
int array1[] = {3, 4, 5, 1, 2};
Test(array1, sizeof(array1) / sizeof(int), 1);
// 有重复数字,并且重复的数字刚好的最小的数字
int array2[] = {3, 4, 5, 1, 1, 2};
Test(array2, sizeof(array2) / sizeof(int), 1);
// 有重复数字,但重复的数字不是第一个数字和最后一个数字
int array3[] = {3, 4, 5, 1, 2, 2};
Test(array3, sizeof(array3) / sizeof(int), 1);
// 有重复的数字,并且重复的数字刚好是第一个数字和最后一个数字
int array4[] = {1, 0, 1, 1, 1};
Test(array4, sizeof(array4) / sizeof(int), 0);
// 单调升序数组,旋转0个元素,也就是单调升序数组本身
int array5[] = {1, 2, 3, 4, 5};
Test(array5, sizeof(array5) / sizeof(int), 1);
// 数组中只有一个数字
int array6[] = {2};
Test(array6, sizeof(array6) / sizeof(int), 2);
// 输入NULL
Test(NULL, 0, 0);
return 0;
}
//***********************7月12日********************************************************//
// 面试题8.cpp : 定义控制台应用程序的入口点。
//
//旋转数组的最小数字
#include "stdafx.h"
#include <exception>
using namespace std;
int Min_1(int* numbers, int length)
{
if(numbers == NULL || length < 0)
throw new std::exception("Invalid parameters");
int index1 = 0; //第一个指针
int index2 = length - 1; //第二个指针
int indexMid = index1;
while(numbers[index1] >= numbers[index2])
{
if(index2 - index1 == 1 )
{
indexMid = index2;
break;
}
if (index1 == index2)
{
indexMid = index2;
break;
}
indexMid = (index1 + index2) / 2; //中间元素
if (numbers[indexMid] >= numbers[index1])
index1 = indexMid;
else if (numbers[indexMid] <= numbers[index2])
index2 = indexMid;
}
return numbers[indexMid];
}
int MinInOrder(int* numbers, int index1, int index2);
int Min_2(int* numbers, int length)
{
if(numbers == NULL || length <= 0)
throw new std::exception("Invalid parameters");
int index1 = 0;
int index2 = length -1;
int indexMid = index1;
while(numbers[index1] >= numbers[index2])
{
indexMid = (index1 + index2) / 2; //中间的元素
//如果这指针指向的3个元素相等,那么只能顺序查找了
if(numbers[index1] == numbers[index2] && numbers[indexMid] == numbers[index1])
{
return MinInOrder(numbers, index1, index2);
}
if(numbers[indexMid] >= numbers[index1])
index1 = indexMid;
else if(numbers[indexMid] <= numbers[index2])
index2 = indexMid;
}
}
//顺序查找
int MinInOrder(int* numbers, int index1, int index2)
{
int result = numbers[index1];
for(int i = index1 + 1; i <= index2; ++i)
{
if(result > numbers[i])
result = numbers[i];
}
return result;
}
/************测试1*************/
void Test1()
{
int numbers[] = {3,4,5,6,7,8,9,-2,-1,1,2};
int min = Min_1(numbers, sizeof(numbers)/sizeof(int));
printf("min = %d\n", min);
}
//int numbers[] = {1,2,3,4,5,6}
void Test2()
{
int numbers[] = {1,2,3,4,5,6}; //0次旋转
int min = Min_1(numbers, sizeof(numbers)/sizeof(int));
printf("min = %d\n", min);
}
//int numbers[]={111111}
void Test3()
{
int numbers[] = {1,1,1,1,1,1};
int min = Min_1(numbers, sizeof(numbers)/sizeof(int));
printf("min = %d\n", min);
}
//int numbers[]={111000}
void Test4()
{
int numbers[] = {1,1,1,0,0,0};
int min = Min_1(numbers, sizeof(numbers)/sizeof(int));
printf("min = %d\n", min);
}
//int numbers[]={111-211}
void Test5()
{
int numbers[] = {1,1,1,-2,1,1};
int min = Min_1(numbers, sizeof(numbers)/sizeof(int));
printf("min = %d\n", min);
}
//int numbers[]={1-21111}
void Test6()
{
int numbers[] = {1,-2,1,1,1,1};
int min = Min_1(numbers, sizeof(numbers)/sizeof(int));
printf("min = %d\n", min);
}
//int numbers[]={1}
void Test7()
{
int numbers[] = {1};
int min = Min_1(numbers, sizeof(numbers)/sizeof(int));
printf("min = %d\n", min);
}
//int numbers[]={1}
void Test8()
{
try
{
int min = Min_1(NULL, 0);
printf("min = %d\n", min);
}
catch(...)
{
printf("无效参数\n");
}
}
/*********测试2*********/
//int numbers[]={1}
void Test1_1()
{
int numbers[] = {1,-2,1,1,1,1};
int min = Min_2(numbers, sizeof(numbers)/sizeof(int));
printf("min = %d\n", min);
}
int _tmain(int argc, _TCHAR* argv[])
{
//Test1();
//Test2();
//Test3();
//Test4();
//Test5();
Test6();
//Test7();
Test8();
Test1_1();
return 0;
}