C++ 和 Python 實現旋轉陣列的最小數字
(說明:本文中的 題目 、 題目詳細說明 及 參考程式碼 均摘自 “何海濤《 劍指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 可以到Linux公社資源站下載:
------------------------------------------分割線------------------------------------------
免費下載地址在 http://linux.linuxidc.com/
使用者名稱與密碼都是 www.linuxidc.com
具體下載目錄在/2019年資料/2月/4日/C++ 和 Python 實現旋轉陣列的最小數字/
下載方法見 http://www.linuxidc.com/Linux/2013-07/87684.htm
------------------------------------------分割線------------------------------------------
Linux公社的RSS地址 : https://www.linuxidc.com/rssFeed.aspx
本文永久更新連結地址: https://www.linuxidc.com/Linux/2019-02/156711.htm