題目一:在一個長度為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))
