題目一:在一個長度為n的數組里的所有數字都在0~n-1的范圍內。數組中某些數字是重復的,但是不知道有幾個數字重復了,也不知道每個數字重復了幾次。請找出數組中任意一個重復的數字。例如,如果輸入長度為7,的數組{2,3,1,0,2,5,3},那么對應的輸出是重復的數組2或者3。(n個元素,n種可能的取值)
解法一:先對數組進行排序,然后再查找排序后的數組中的重復元素
def selectedSort(lis): for i in range(0, len(lis)): minVal = lis[i] minInd = i #找出最小的元素 for j in range(i+1,len(lis)): if minVal>=lis[j]: minVal=lis[j] minInd=j #交換 lis[minInd]=lis[i] lis[i]=minVal return lis lis=[8,5,3,3,7,6,3,9,1,8] lis_order=selectedSort(lis) print(lis)
解法二:開辟一個新數組B,每次掃描源數組A中的元素,如果不在B中,就加入B中,如果在B中,就找到一個重復的元素
lisA=[8,5,3,3,7,6,3,9,1,8] def findDuplicates(lisA): lisB = [] for i in lisA: if i in lisB: print("找到一個重復的元素:%d"%i) break else: #當前掃描的元素不在lisB中,就加入到lisB中 lisB.append(i) continue findDuplicates(lisA)
解法三:因為列表總共有n個元素,所有元素可能取到的元素有0~n-1,共n種。如果不存在重復的數字,那么排序后數字i將會出現在下標為i的位置。現在讓我們重新掃描數組,
- 當掃描到下標為i的數字時,首先比較這個數字(記為m)與i是否相等:
- 如果是,繼續掃描下一個元素,
- 如果不是,則再拿它與第m個數字比較:
- 如果它和第m個數字相同,就找到了一個重復的元素;
- 如果不同,就將m與第m個數字互換。接下來繼續重頭開始,重復換這個比較。
lisA=[8,0,2,3,7,6,4,2,1,5] def findDuplicates(lisA): i=0 while i <len(lisA) and lisA!=[]: m = lisA[i] if m == i: i += 1 else: if m == lisA[m]: print('找到一個重復的元素:%d' % m) break else: temp = lisA[i] lisA[i] = lisA[m] lisA[m] = temp i = 0 findDuplicates(lisA)
題目二:
在一個長度為n+1的數組里的所有數字都在1~n范圍內,所以數字中至少有一個數字是重復的。請找出數組中任意一個重復的數字,但是不能修改數組。例如,如果輸入長度為8的數組{2,3,5,4,3,2,6,7},那么對應的輸出是重復的數字2或3。(n+1個元素,n種可能的取值)
解法一:同上題解法二,開辟一個大小為n+1的新數組
解法二:避免使用O(n)的輔助空間。我們把取值空間[1,n]從中間的數字m分為兩部分,前面一部分為1~m,后面一部分為m+1~n。如果數組中元素落在前面一部分的元素個數多於m個,那么數組中重復的元素一定落在前半區間;否則,數組中重復的元素一定落在后半區間。然后,我們可以繼續將包含重復元素的區間一分為二,直到找到一個重復的元素。
lisA=[8,1,2,3,7,6,7,4,9,5,10] def findDuplicates(lisA): """找到重復元素,返回True;否則,返回False""" low=1 high=len(lisA)-1 while low<=high: mid=(low+high)//2 #統計數組中落在前半部分區間中的元素的個數 count_low=0 for i in lisA: if i in range(low,mid+1): count_low+=1 #判斷落在長度為1的區間中的數組元素個數 if high==low: if count_low>1: #如果大於1,則找到重復的元素 return low else: break #比較前半部分區間長度與落在該區間內元素的個數,決定將前半部分/后半部分區間繼續一分為二 if count_low>(mid-low+1): high=mid else: low=mid+1 return False print(findDuplicates(lisA))