剑指offer面试题8

面试题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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值