[英]Iterating over a list to find whether an element is greater than all following elements
我目前很難嘗試計算列表中每個元素大於其所有后續元素的次數。
我嘗試用兩個例子編寫解決方案,但沒有成功。 我希望能夠用兩個循環或一個循環來做到這一點。 任何采用蠻力方法的想法都將不勝感激。 這是我到目前為止所擁有的。
兩個循環
def count_dominators(items):
count=0
for i in range(len(items)):
for j in range(len(items)-1):
if items[j]>items[i]:
break
if j == len(items)-1:
count+=1
if i == len(items)-1:
count+=1
return count
items=[42, 7, 12, 9, 2, 5]
count_dominators(items)
一個循環
def count_dominators(items):
count=0
largest=items[-1]
i=len(items)-1
while i>=0:
if items[i] > largest:
count+=1
i-=1
return count
items=[42, 7, 12, 9, 2, 5]
print(count_dominators(items))
這是任務:
計數支配者
def count_dominators(items):
如果
items
元素右側的每個元素(不僅僅是緊鄰其右側的一個元素)都嚴格小於它,則稱該元素是主導元素。 根據這個定義,列表的最后一項自動成為支配者。 這個 function 應該計算items
中的元素有多少是支配者,並返回該計數。 例如,[42, 7, 12, 9, 13, 5]
的支配元素是其元素42
、13
和5
。在開始為此函數編寫代碼之前,您應該查閱“ 畫家 Shlemiel ”的寓言,並思考這個來自更簡單時代的看似愚蠢的故事如何與今天在列表、字符串和其他序列上執行的計算問題相關聯。 這個問題將是您在本課程期間和之后將遇到的許多問題中的第一個,以說明僅使用一個循環即可在極短時間內實現 Shlemiel 使用兩個嵌套循環實現的相同最終結果的重要原則。 因此,您的工作量僅相對於
items
數線性增加,而 Shlemiel 來回的總時間呈二次方增長,即 function 項目數的平方。
items
預期結果 [42, 7, 12, 9, 2, 5]
4
[]
0
[99]
1
[42, 42, 42, 42]
1
range(10**7)
1
range(10**7, 0, -1)
10000000
試圖在 function 調用中隱藏一些 Shlemiel 算法的內部循環(這包括 Python 內置函數,例如
max
和列表切片)並假裝這不知何故使這些內部循環花費恆定的時間只會召喚 Compubook 標題之神帶着喧囂返回,以索取他們的 linon 執行時間份額。
這是您的第二個改進版本(對每個更改和修復都有評論):
def count_dominators(items):
# - start with 1 smaller than last item
# - handle empty list
largest = items[-1] - 1 if items else 0
count = 0
# - use for loop
# - use reversed(...)
for item in reversed(items):
if item > largest:
count += 1
# - update largest
largest = item
return count
讓我們先 go 解決天真的解決方案:
考慮列表索引i
處的元素。 如果該元素大於索引大於i
的所有元素,則該元素是“支配者”
def count_dominators(items):
count = 0
for i in range(len(items)):
is_dom = True # Assume for now that it is a dominator
for j in range(i+1, len(items)): # You only need elements after the ith element
if items[i] <= items[j]: # If elem at i is less than j, this is not a dominator
is_dom = False
break # So we don't need to check any more j for this i
if is_dom: # Only increment the count if this is a dominator
count += 1
return count
該解決方案以二次時間運行。
現在尋求更智能的解決方案。 我不熟悉畫家 Shlemiel 的寓言,但一個簡單的解決方案是創建另一個列表,其中該列表索引i
處的元素是子列表items[i+1:]
的最大值。 然后,如果原始列表的第i
個元素大於新列表的第i
個元素,我們就知道它是支配者。 由於我們不想隱藏max()
調用中的復雜性,我們可以向后遍歷列表。
largest_vals = [0] * len(items)
largest_vals[-1] = items[-1] - 1
largest = items[-1]
for index in range(len(items)-2, -1, -1): # Iterate backwards
if items[index] > largest:
largest = items[index]
largest_vals[index] = largest
接下來,定期遍歷列表,並計算有多少元素大於它們對應的largest_vals
:
count = 0
for elem, lv in zip(items, largest_vals):
if elem > lv:
count += 1
作為 function:
def count_dominators(items):
if not items: return 0
largest_vals = [0] * len(items)
largest_vals[-1] = items[-1] - 1 # To handle the case of the last element
largest = items[-1]
for index in range(len(items)-2, -1, -1): # Iterate backwards from the second-to-last element to the first
if items[index+1] > largest: # Only overwrite largest if the current element is bigger
largest = items[index+1]
largest_vals[index] = largest
count = 0
for elem, lv in zip(items, largest_vals):
if elem > lv:
count += 1
return count
當然,您可以通過簡單地檢查原始循環中的items[index] > largest
來將其簡化為單個循環,但是如果將它們分開,則有助於可視化問題。
要測試這個:
ls = [([42,7,12,9,2,5], 4), ([], 0), ([99], 1), ([42, 42, 42, 42], 1), (list(range(10**7)), 1), (list(range(10**7, 0, -1)), 10**7)]
for input_val, expected_val in ls:
print(count_dominators(input_val), expected_val)
這表明 function 返回所有測試用例的預期值
4 4
0 0
1 1
1 1
1 1
10000000 10000000
像您嘗試過的那樣倒退以及 Shlomo 的做法是最好的。 如果可以go倒退。 當您不能時,這里有一個替代方法,即當您有一個只能向前迭代的可迭代對象時。
def count_dominators(items):
doms = []
for item in items:
while doms and item >= doms[-1]:
doms.pop()
doms.append(item)
return len(doms)
我保留了一個(遞減的)列表,列出了目前所見項目的支配者。 例如,如果那是[56, 13, 2]
然后我看到41
,它會在添加為新的默認支配者之前從支配者中刪除2
和13
。 那么支配者列表是[56, 41]
。
運行時間為 O(n)。 你可能會想“但是它有兩個嵌套循環,賦值不是告訴我這需要二次方時間嗎?” . 好吧,不,不是這種嵌套循環。 內部循環彈出一個項目,因為我們append每個項目只彈出一次,所以我們也最多彈出每個項目一次。 所以總的來說,內循環最多運行n次。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.