劍指offer:旋轉陣列的最小數字(Python)
題目描述
把一個數組最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。 輸入一個非遞減排序的陣列的一個旋轉,輸出旋轉陣列的最小元素。 例如陣列{3,4,5,1,2}為{1,2,3,4,5}的一個旋轉,該陣列的最小值為1。 NOTE:給出的所有元素都大於0,若陣列大小為0,請返回0。
解題思路
思路1
暴力解法:根據給定的陣列特點,從左到右遍歷陣列元素,當首次遇到陣列中某個元素比上一個元素小時,該元素就是我們需要的元素。
python程式碼
defminNumberInRotateArray(self, rotateArray):
if not rotateArray:
return 0
num = rotateArray[0]
for i in range(1,len(rotateArray)):
if rotateArray[i] >= num:
num = rotateArray[i]
else:
return rotateArray[i]
思路2
思路1的做法太無腦,時間複雜度O(N)。旋轉陣列也算是一種排序陣列,可以用二分法解題,使時間複雜度最低控制在O(lgN)。
由於最近遞迴用的比較多,本能的就想通過遞迴求解該題。具體思路是用變數mid定位到陣列的中間位置,將陣列頭部的值與mid處的值進行比較,不斷縮小陣列。這裡有兩個需要注意的點:
1. 由於是旋轉陣列,縮小到最小的陣列大小為2,且陣列中第一個元素為陣列中值最大的元素,第二個元素是陣列中值最小的元素,最後返回第二個元素。
2. 當陣列中包含重複元素,具體到程式碼中是:當mid處的值等於陣列頭部的值時,無法判斷最小值位於mid左側還是右側,此時只能採取遍歷的方式。
Python程式碼
def minNumberInRotateArray(self, rotateArray):
if not rotateArray:
return 0
if len(rotateArray)==2:
return rotateArray[1]
mid = int(len(rotateArray)/2)
if rotateArray[mid] > rotateArray[0]:
return self.minNumberInRotateArray(rotateArray[mid:])
elif rotateArray[mid] < rotateArray[0]:
return self.minNumberInRotateArray(rotateArray[:mid+1])
else:
for i in range(1,len(rotateArray)):
if rotateArray[i] < rotateArray[0]:
return rotateArray[i]
return rotateArray[0]
思路3
思路3屬於思路2的小優化,可以縮短程式碼的長度,但不能改變程式碼的時間複雜度。具體來說,當mid處的值等於陣列頭部的值時,即當代碼需要遍歷時,可用一行程式碼代替for迴圈:
def minNumberInRotateArray(self, rotateArray):
if not rotateArray:
return 0
if len(rotateArray)==2:
return rotateArray[1]
mid = int(len(rotateArray)/2)
if rotateArray[mid] > rotateArray[0]:
return self.minNumberInRotateArray(rotateArray[mid:])
elif rotateArray[mid] < rotateArray[0]:
return self.minNumberInRotateArray(rotateArray[:mid+1])
else:
return self.minNumberInRotateArray(rotateArray[1:])
思路4
由於每次遞迴都需要傳入陣列,這無疑更佔用儲存空間,特別當陣列特別長時。最後給出該方法的非遞迴版本,只有一個數組不變,變化的只有指向陣列的指標指向。
def minNumberInRotateArray(self, rotateArray):
left = 0
right = len(rotateArray)-1
while left < right:
mid = int((left+right)/2)
if rotateArray[mid] > rotateArray[right]:
left = mid+1
elif rotateArray[mid] < rotateArray[right]:
right = mid
else:
right -= 1
return rotateArray[left]
雖然思路4程式碼簡短,時間複雜度和空間複雜度都比較低,但是如果稍微不注意,還是會掉到小坑裡,比如如果用mid處的值和陣列左邊的值比較,會有什麼不同;else語句中如果不讓right處的指向左移一位,而是讓left處的指標右移一位會怎麼樣,都是需要稍加留意的地方。