1. 程式人生 > >旋轉數組的最小數字(C++ 和 Python 實現)

旋轉數組的最小數字(C++ 和 Python 實現)

ram 兩個 requires images red 輸入 off internet iat

(說明:本博客中的題目題目詳細說明參考代碼均摘自 “何海濤《劍指Offer:名企面試官精講典型編程題》2012年”)

題目

  把一個數組最開始的若幹個元素搬到數組的末尾,我們稱之為數組的旋轉。輸入一個遞增排序的數組的一個旋轉,輸出旋轉數組的最小元素。例如數組 {3, 4, 5, 1, 2} 為 {1, 2, 3, 4, 5} 的一個旋轉,該數組的最小值為 1 。

算法設計思想

1. 暴力查找(Bruteforce Search):把旋轉數組從前到後遍歷一遍,其時間復雜度為 O(n)。很明顯,這種思想非常直接粗暴,沒有用到旋轉數組的性質。

2. 二分查找(Binary Search):每次查找都把旋轉數組平均分成兩部分,通過比較當前旋轉數組兩端點

中間點的值,判斷最小值在數組的哪一部分,從而達到縮小搜索範圍的目的。其中,兩端點為當前的旋轉數組索引最小索引最大的元素,中間點為這兩部分子數組的連結元素,也可以看做為軸樞點(pivot point),這裏取旋轉數組的最小索引和最大索引的算術平均值(向下取整)作為索引,其對應的元素作為中間點。具體過程,如圖 2.10 所示。

技術分享

需要註意,當旋轉數組的兩端點的值都與中間點的值相等時,因為無法判斷最小值在哪一部分,因此需要采用順序查找方法,即暴力查找。其示例如圖 2.11 所示。

技術分享

C++ 實現

#include <stdio.h>
#include <exception>
#include 
<iostream> // Declare a parameter exception class ParamException: public std::exception { virtual const char* what() const throw() { return "Error: input parameters exception!"; } }; // find minimum in data[staIndex..endIndex] int findMinInRotatedArray(int data[], int staIndex, int
endIndex) { if (staIndex > endIndex || data == NULL) { throw ParamException(); } int minimum = data[staIndex]; if (data[staIndex] >= data[endIndex] && staIndex < endIndex - 1) // 易錯點 { // Use Binary Search int midIndex = (staIndex + endIndex) / 2; if (data[midIndex] > data[staIndex]) minimum = findMinInRotatedArray(data, midIndex, endIndex); else if (data[midIndex] < data[endIndex]) minimum = findMinInRotatedArray(data, staIndex, midIndex); else { // Find the minimum sequentially for (int i = staIndex+1; i <= endIndex; ++i) if (minimum > data[i]) minimum = data[i]; } } else if (staIndex == (endIndex - 1)) { minimum = data[staIndex] > data[endIndex]? data[endIndex]:data[staIndex]; } return minimum; } void unitest() { int arr1[] = {3, 4, 5, 1, 2}; int arr2[] = {1, 0, 1, 1, 1}; int arr3[] = {1, 1, 1, 0, 1}; int arr4[] = {1, 2, 3, 4, 5}; printf("The minimum of the rotated array {3, 4, 5, 1, 2} is %d.\n", findMinInRotatedArray(arr1, 0, 4)); printf("The minimum of the rotated array {1, 0, 1, 1, 1} is %d.\n", findMinInRotatedArray(arr2, 0, 4)); printf("The minimum of the rotated array {1, 1, 1, 0, 1} is %d.\n", findMinInRotatedArray(arr3, 0, 4)); printf("The minimum of the rotated array {1, 2, 3, 4, 5} is %d.\n", findMinInRotatedArray(arr4, 0, 4)); // Test index parameter exception try { findMinInRotatedArray(arr3, 5, 4); } catch(std::exception& e) { std::cout << e.what() << std::endl; }; } int main(void) { unitest(); return 0; }

Python 實現

#!/usr/bin/python
# -*- coding: utf8 -*-

# Define ParamError Exception
class ParamError(Exception):
    def __init__(self, value):
        self.value = value
    def __str__(self):
        return repr(self.value)


# Find the minimum in rotated array    
def min_in_rotated_array(data, length):
    if data is None or length <= 0:
        raise ParamError("Error: input parameters exception!")
    # Index initialization
    sta, mid, end = 0, 0, length-1
    # Ensure this requisite before binary search
    while data[sta] >= data[end]:
        if end - sta == 1:
            mid = end
            break
        # Get the middle index
        mid = (sta + end) / 2
        # Find the minimum in order
        if (data[sta] == data[mid]) and (data[mid] == data[end]):
            minimum = data[sta]
            for i in range(sta+1, end+1):
                if minimum > data[i]:
                    minimum = data[i]
            return minimum

        if data[sta] <= data[mid]:
            sta = mid
        elif data[end] >= data[mid]:
            end = mid

    return data[mid]


def unitest():
    arr1 = [3, 4, 5, 1, 2]
    arr2 = [1, 0, 1, 1, 1]
    arr3 = [1, 1, 1, 0, 1]
    arr4 = [1, 2, 3, 4, 5]

    print("The minimum of the rotated array [3, 4, 5, 1, 2] is %d." % min_in_rotated_array(arr1, 5));
    print("The minimum of the rotated array [1, 0, 1, 1, 1] is %d." % min_in_rotated_array(arr2, 5));
    print("The minimum of the rotated array [1, 1, 1, 0, 1] is %d." % min_in_rotated_array(arr3, 5));
    print("The minimum of the rotated array [1, 2, 3, 4, 5] is %d." % min_in_rotated_array(arr4, 5));

    try:
        min_in_rotated_array(arr1, -2)
    except Exception, e:
        print "\nFunction call: min_in_rotated_array(arr1, -2)"
        print e


if __name__ == __main__:
    unitest()

參考代碼

1. targetver.h

#pragma once

// The following macros define the minimum required platform.  The minimum required platform
// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run 
// your application.  The macros work by enabling all features available on platform versions up to and 
// including the version specified.

// Modify the following defines if you have to target a platform prior to the ones specified below.
// Refer to MSDN for the latest info on corresponding values for different platforms.
#ifndef _WIN32_WINNT            // Specifies that the minimum required platform is Windows Vista.
#define _WIN32_WINNT 0x0600     // Change this to the appropriate value to target other versions of Windows.
#endif

2. stdafx.h

// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once

#include "targetver.h"

#include <stdio.h>
#include <tchar.h>



// TODO: reference additional headers your program requires here

3. stdafx.cpp

// stdafx.cpp : source file that includes just the standard includes
// MinNumberInRotatedArray.pch will be the pre-compiled header
// stdafx.obj will contain the pre-compiled type information

#include "stdafx.h"

// TODO: reference any additional headers you need in STDAFX.H
// and not in this file

4. MinNumberInRotatedArray.cpp

// MinNumberInRotatedArray.cpp : Defines the entry point for the console application.
//

// 《劍指Offer——名企面試官精講典型編程題》代碼
// 著作權所有者:何海濤

#include "stdafx.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 _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. 參考代碼下載

項目 08_MinNumberInRotatedArray 下載: 百度網盤

何海濤《劍指Offer:名企面試官精講典型編程題》 所有參考代碼下載:百度網盤

參考資料

[1] 何海濤. 劍指 Offer:名企面試官精講典型編程題 [M]. 北京:電子工業出版社,2012. 63-71.

旋轉數組的最小數字(C++ 和 Python 實現)