《剑指Offer》面试题8:旋转数组的最小数字

《剑指Offer》面试题8:旋转数组的最小数字

1 快速排序


include <ctime> // ctime  
#include <iostream>
using namespace std;

//生成low和high之间的随机数
int RandomInRange(int low,int high)
{
    srand(time(NULL));
    int rnum = rand() % (high - low + 1) + low;
    return  rnum;
}

int Partition(int data[], int length, int start, int end)
{
    if (data == NULL || length <= 0 || start < 0 || end >= length)
        throw new exception("Invalid Parameters");
    int index = RandomInRange(start, end);
    swap(data[index], data[end]);
    int small = start - 1;
    for (int index = start; index < end; ++index)
    {
        if (data[index] < data[end])
        {
            ++small;
            if (small!=index)
                swap(data[index], data[small]);
        }
    }
    ++small;
    swap(data[small], data[end]);
    return small;
}

    接下来,用递归实现快速排序。

void QuickSort(int data[], int length, int start, int end)
{
	if (start == end)
		return;
	int index = Partition(data, length, start, end);
	if (index>start)
		QuickSort(data, length, start, index - 1);
	if (index<end)
		QuickSort(data, length, index+1, end);
}
int main()
{
	int data[] = { 3, 8, 7, 1, 2, 5, 6, 4 };
	int length = 8;
	int start = 0;
	int end = 7;
	QuickSort(data, length, start, end);
	for (int i = 0; i < length; i++)
	{
		cout << data[i];
	}
	system("pause");
	return 0;
}

2 对公司年龄排序


#include <iostream>
using namespace std;
void SortAges(int ages[], int length)
{
	if (ages == NULL || length <= 0)
		return;
	const int oldestAge = 99;
	int timesOfAge[oldestAge + 1];
	for (int i = 0; i <= oldestAge; i++)
		timesOfAge[i] = 0;
	for (int i = 0; i < length; i++)
	{
		int age = ages[i];
		if (age<0 || age>oldestAge)
			throw new exception("age out of range.");
		++timesOfAge[age];

	}
	int index = 0;
	for (int i = 0; i <= oldestAge; ++i)
	{
		for (int j = 0; j < timesOfAge[i]; j++)
		{
			ages[index] = i;
			index++;
		}
	}
}

3 面试题8:旋转数组的最小数字

知识点

    二分查找

题目描述

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

解题思路





测试用例


代码(原书)

/* 《剑指Offer——名企面试官精讲典型编程题》代码
 著作权所有者:何海涛 */

#include "stdio.h"
#include<exception>

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");

	int index1 = 0;
	int index2 = length - 1;
	int indexMid = index1;
	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 main()
{
	// 典型输入,单调升序的数组的一个旋转
	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);
	system("pause");
	return 0;
}


代码(牛客网)

class Solution {
public:
    int minNumberInRotateArray(vector<int> rotateArray) {
        if(rotateArray.empty())          //若数组大小为0,返回0
            return 0;
        int length = rotateArray.size();
        int index1 = 0;
        int index2 = length-1;
        int indexMid = index1;          //若将0个元素搬到末尾,即仍为原数组,此时数组的第一个数字即为最小数字,故将indexMid初始化为index1
        while(rotateArray[index1] >= rotateArray[index2])    // 若旋转
        {
            if(index2 - index1 == 1)                        //分界点
            {
                indexMid = index2;
                break;
            }
            indexMid = (index1 + index2)/2;
            
            // rotateArray[index1] rotateArray[index2] rotateArray[indexMid]三者相等
            // 无法确定中间元素是属于前面还是后面的递增子数组
            // 只能顺序查找
            if(rotateArray[index1] == rotateArray[index2] && rotateArray[index1] == rotateArray[indexMid])
            {
                // 顺序寻找最小值
                int minNumber = rotateArray[index1];
                for(int i = index1 + 1; i <= index2; i++)
                {
                    if(minNumber > rotateArray[i])
                        minNumber = rotateArray[i];
                }
                indexMid = minNumber;
            }
            // 中间元素位于前面的递增子数组
            // 此时最小元素位于中间元素的后面
            if(rotateArray[indexMid] >= rotateArray[index1])
                index1 = indexMid;
             // 中间元素位于后面的递增子数组
            // 此时最小元素位于中间元素的前面
            if(rotateArray[indexMid] <= rotateArray[index2])
                index2 = indexMid;
        }
        return rotateArray[indexMid];
        
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值