初學Python,代碼雖能準確運行,可是語言的使用方面可能不簡潔或不許確,但願各位前輩指正。
python
層層比較,不贅述,時間複雜度O(n^2)。算法
1 def bubble_sort(a_list): 2 for last in range(len(a_list)-1,0,-1): 3 exchange=0 4 for i in range(0,last): 5 if a_list[i]>a_list[i+1]: 6 temp=a_list[i] 7 a_list[i]=a_list[i+1] 8 a_list[i+1]=temp 9 exchange +=1 10 if exchange==0: 11 break 12 13 測試: 14 a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] 15 bubble_sort(a_list) 16 print(a_list) 17 輸出: 18 [17, 20, 26, 31, 44, 54, 55, 77, 93]
二、選擇排序( selection sort)
該算法依次找出最大、次大……的元素並置於應屬的位置,與冒泡排序的實現效果相同,只不過選擇排序只是在每一輪選擇結束後交換一次元素,減小了交換元素的次數。但比較次數沒有減小,故時間複雜度依然是O(n^2)。shell
1 def selection_sort(a_list): 2 for last in range(len(a_list)-1,0,-1): 3 max=a_list[last] 4 pos=last 5 for i in range(last): 6 if a_list[i]>max: 7 pos=i 8 max=a_list[i] 9 a_list[pos]=a_list[last] 10 a_list[last]=max 11 12 測試: 13 a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] 14 selection_sort(a_list) 15 print(a_list) 16 輸出: 17 [17, 20, 26, 31, 44, 54, 55, 77, 93]
三、插入排序(insertion sort)
倒着比較,將新元素插入已排序列的正確位置,採用遞歸,時間複雜度仍爲O(n^2)。 數組
1 def insertion_sort(a_list): 2 last=len(a_list)-1 3 if last>0: 4 list1=insertion_sort(a_list[:last]) 5 i=len(list1)-1 6 temp=a_list[last] 7 while i>=0: 8 if temp<=list1[i]: 9 i-=1 10 else: 11 break 12 for pos in range(last,i+1,-1): 13 a_list[pos]=list1[pos-1] 14 a_list[i+1]=temp 15 a_list[:i+1]=list1[:i+1] 16 return a_list 17 18 測試: 19 a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] 20 print insertion_sort(a_list) 21 輸出: 22 [17, 20, 26, 31, 44, 54, 55, 77, 93]
選取合適的gap值,將待排序元素數組中相隔gap個位置的元素歸爲一組,總共分紅gap個組,組內排序(用插入排序實現),縮小gap,遞歸調用自身得到更新的排序結果。數據結構
1 def shell_sort(a_list,gap): 2 if gap>0: 3 for i in range(0,gap): 4 for j in range(i+gap,len(a_list),gap): 5 k=j-gap 6 temp=a_list[j] 7 while a_list[k]>temp and k>=i: 8 a_list[k+gap]=a_list[k] 9 k-=gap 10 a_list[k+gap]=temp 11 shell_sort(a_list,gap//2) 12 測試: 13 a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] 14 shell_sort(a_list,4) 15 print(a_list) 16 輸出: 17 [17, 20, 26, 31, 44, 54, 55, 77, 93]
把待排序數組分紅兩部分分別排序,以遞歸調用自身實現,對已排好的兩部分比較合併。app
1 def merge_sort(a_list): 2 if len(a_list)>1: 3 split=len(a_list)//2 4 left_list=merge_sort(a_list[0:split]) 5 right_list=merge_sort(a_list[split:len(a_list)]) 6 i=0 7 j=0 8 k=0 9 while i <len(left_list) and j <len(right_list): 10 if left_list[i]<right_list[j]: 11 a_list[k]=left_list[i] 12 i+=1 13 k+=1 14 else: 15 a_list[k]=right_list[j] 16 j+=1 17 k+=1 18 if i==len(left_list): 19 a_list[k:]=right_list[j:] 20 if j==len(right_list): 21 a_list[k:]=left_list[i:] 22 return a_list 23 測試: 24 a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] 25 print merge_sort(a_list) 26 輸出: 27 [17, 20, 26, 31, 44, 54, 55, 77, 93]
六、快速排序(quick sort) dom
採用分治思想,每次選取list中的第一個元素做爲主元(pivot),經過比較找到其應屬的位置,將原list劃分爲左右兩個list,分別遞歸調用自身。ide
1 def quick_sort(a_list): 2 if len(a_list)>1: 3 pivot=a_list[0] 4 i=0 5 j=1 6 while j<len(a_list): 7 if a_list[j]<pivot: 8 i+=1 9 temp=a_list[i] 10 a_list[i]=a_list[j] 11 a_list[j]=temp 12 j+=1 13 a_list[0]=a_list[i] 14 a_list[i]=pivot 15 a_list=quick_sort(a_list[:i])+[a_list[i]]+quick_sort(a_list[i+1:]) 16 return a_list 17 18 測試: 19 a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20] 20 print quick_sort(a_list) 21 輸出: 22 [17, 20, 26, 31, 44, 54, 55, 77, 93]
下面附上隨機化快排中隨機劃分的部分Python代碼:函數
1 import random 2 def random_partition(a_list): 3 p=random.randint(0,len(a_list)-1) 4 pivot=a_list[p] 5 a_list[p]=a_list[0] 6 a_list[0]=pivot 7 i=0 8 j=1 9 while j<len(a_list): 10 if a_list[j]<pivot: 11 i+=1 12 a_list[i],a_list[j]=a_list[j],a_list[i] 13 j+=1 14 a_list[0],a_list[i]=a_list[i],a_list[0] 15 return (pivot,i+1,a_list[:i],a_list[i+1:]) 16 17 測試: 18 a_list=[2,4,13,5,8,6,14,7] 19 pivot,i,list_left,list_right=random_partition(a_list) 20 print (pivot,i,list_left,list_right) 21 輸出: 22 (13, 7, [7, 4, 2, 5, 8, 6], [14])
七、計數排序(count sort)
不是比較排序類算法,沒有Ω(nlogn)時間複雜度限制,但要求待排序元素爲非負整數。選擇合適的待排序數的數值上限k,建立大小爲k的list C,待排元素的數值做爲C的index,在C[index]裏記錄該元素出現的次數,把C中某一index前的全部元素求和即爲index對應的待排元素應屬的位置。線性時間複雜度。 學習
1 def count_sort(a_list,k): 2 B=[None]*len(a_list) # ordered list 3 C=[0]*k # helper list to count 4 for i in a_list: 5 C[i]+=1 6 for j in range(1,k): 7 C[j]=C[j-1]+C[j] 8 for p in range(len(a_list)-1,-1,-1): 9 B[C[a_list[p]]-1]=a_list[p] # the index of a list starts from 0, not 1 10 C[a_list[p]]-=1 11 return B 12 13 a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20, 26, 93] 14 print count_sort(a_list,100)
八、基數排序(radix sort)
基本思想是,對於n個r進制k位數進行排序,採用穩定排序算法,先依低位排序再依高位排序,以此保證依高位排序時,相同高位的數可保持其依低位已排好的次序。時間複雜度爲O(k(n+r))。
下面代碼以十進制數的排序爲例。可是代碼中的兩個問題我沒有想明白,如Question1和Question2描述,但願各位前輩指點~
1 def radix_sort(a_list,k): 2 B=[0]*len(a_list) 3 for j in range(1,k+1): 4 C=[0]*10 # helper list to count 5 # A=[(i%10**j)//(10**(j-1)) for i in a_list] # Question1: why is this way wrong to def A? 6 A=[] 7 for t in a_list: 8 A.append((t%10**j)//(10**(j-1))) 9 for i in A: 10 C[i]+=1 11 for q in range(1,10): 12 C[q]=C[q-1]+C[q] 13 for p in range(len(A)-1,-1,-1): 14 B[C[A[p]]-1]=a_list[p] # the index of a list starts from 0, not 1 15 C[A[p]]-=1 16 a_list=B 17 B=[0]*len(a_list) # Question2: why must B set to be 0 here ???? 18 return a_list 19 20 a_list = [54, 26, 93, 17, 77, 31, 44, 55, 20, 26, 93] 21 print radix_sort(a_list,2)
九、桶排序(bucket sort)
桶排序的原始定義針對(0,1)範圍內均勻分佈的數,下面代碼也是針對這種狀況編寫的。
1 from insertion_sort import insertion_sort 2 import random 3 def bucket_sort(a_list): 4 bucket=[[] for i in range(len(a_list))] # [[]]*len(list) is shallow copy, wrong 5 for i in a_list: 6 if i== 1: 7 i= 0.99 8 bucket[int(i*len(a_list))].append(i) 9 a_list=[] 10 for j in bucket: 11 a_list=a_list+insertion_sort(j) 12 return a_list 13 測試: 14 a_list=[random.uniform(0,1) for i in range(13)] 15 print 'before sort: \n %s '% a_list 16 print 'after sort: \n %s ' % bucket_sort(a_list) 17 輸出: 18 before sort: 19 [0.6017131497978297, 0.5257088814737517, 0.5308222308002468, 0.9233128021687357, 0.8701655171999804, 0.5526799446055798, 0.11875967581091884, 0.9175286740805743, 0.8919451407674414, 0.5193946279480275, 0.8656422841387905, 0.7077658852432671, 0.7646975175708084] 20 after sort: 21 [0.11875967581091884, 0.5193946279480275, 0.5257088814737517, 0.5308222308002468, 0.5526799446055798, 0.6017131497978297, 0.7077658852432671, 0.7646975175708084, 0.8656422841387905, 0.8701655171999804, 0.8919451407674414, 0.9175286740805743, 0.9233128021687357]
桶排序算法的核心思想是經過合理設置桶,來知足每一個桶所含元素數的平方和的指望與輸入規模成線性關係,達到線性指望時間複雜度。
直觀上理解,我認爲就是對分佈較密的數據區域多劃分些桶,對數據分佈稀疏的區域少劃分桶,以達到平均每一個桶內分配常數個元素(原始定義裏分紅n個桶時每一個桶平均分配1個元素)。對通常狀況可採起的辦法是,針對輸入序列超出(0,1)範圍的狀況,可經過歸一操做保證待排成數據在(0,1)範圍內;針對輸入序列非均勻分佈的狀況,可經過某種映射 f 將待排序列映射爲均勻分佈。
以算法導論第二版的習題8.4-5來講明使用該算法的更通常的狀況。
Ex8.4-5 一個隨機變量X的機率分佈函數P(x)定義爲 P(x)=Pr{X<=x}。假設n個隨機變量X1,X2,……,Xn 符合一個連續機率分佈函數P,它能夠在O(1)時間內計算。說明如何在線性指望時間內排序這n個數。
若考慮採用桶排序算法,則桶的大小要依據X的分佈來設置,設n個數分配n個桶,P(x)爲[0,1]上的增函數,爲了使每一個桶中平均分配一個元素,只需將P(x) n均分(即[0,1] n均分),函數值對應的自變量即爲桶劃分的界。對於第i個桶,其範圍( P−1((i−1)/n),P−1(i/n)) 。因爲反函數運算不便,可直接將xi映射爲P(xi)分析。則xi將被放入第⌊P(xi)∗n⌋個桶中。
十、指望線性時間的第k個順序統計量選擇
隨機化選擇主元,分治處理,返回第k個順序統計量。代碼中base的設置是爲了更新當前處理list中的元素在原a_list中的位置。
1 import random 2 def random_partition(a_list): 3 p=random.randint(0,len(a_list)-1) 4 pivot=a_list[p] 5 a_list[p]=a_list[0] 6 a_list[0]=pivot 7 i=0 8 j=1 9 while j<len(a_list): 10 if a_list[j]<pivot: 11 i+=1 12 a_list[i],a_list[j]=a_list[j],a_list[i] 13 j+=1 14 a_list[0],a_list[i]=a_list[i],a_list[0] 15 return (pivot,i+1,a_list[:i],a_list[i+1:]) 16 17 def random_select(a_list,k): 18 base =0 19 list_next=a_list 20 while list_next !=[]: 21 pivot,i,list_left,list_right= random_partition(list_next) 22 i=base+i 23 if i==k: 24 return pivot 25 elif i<k: 26 list_next=list_right 27 base =i 28 else: 29 list_next=list_left 30 return False 31 32 測試: 33 a_list=[2,4,13,5,8,6,14,7] 34 print random_select(a_list,5) 35 輸出: 36 7
十一、最壞狀況線性時間的中位數選擇
隨機化算法能夠得到良好的指望時間複雜度,卻對最壞狀況無能爲力。該求取中位數(偶數指下中位數)的算法優化了主元的選擇方法,將n個元素以每組5個元素分紅⌊n/5⌋組,將每組的中位數組成新的list,遞歸調用自身,可保證獲得的新list的中位數位於該n位有序序列的1/4~3/4位置範圍(還可更精確),杜絕了隨機化算法可能引發的最差狀況,保證了線性時間複雜度。
在下面的partition函數中,爲了獲得主元的index,註釋行用了list.index()函數,可是該方法時間複雜度O(n),怎樣修改才能使partition函數直接傳入主元的index參數呢?求大神幫忙~
1 def insertion_sort(a_list): 2 last=len(a_list)-1 3 if last>0: 4 a_list1=insertion_sort(a_list[:last]) 5 i=len(a_list1)-1 6 temp=a_list[last] 7 while i>=0: 8 if temp<=a_list1[i]: 9 i-=1 10 else: 11 break 12 for pos in range(last,i+1,-1): 13 a_list[pos]=a_list1[pos-1] 14 a_list[i+1]=temp 15 a_list[:i+1]=a_list1[:i+1] 16 return a_list 17 18 def partition(a_list,key): 19 p=a_list.index(key) #Question: such an enbarrasing thing to find the key from a_list, how to do? 20 pivot=a_list[p] 21 a_list[p]=a_list[0] 22 a_list[0]=pivot 23 i=0 24 j=1 25 while j<len(a_list): 26 if a_list[j]<pivot: 27 i+=1 28 a_list[i],a_list[j]=a_list[j],a_list[i] 29 j+=1 30 a_list[0],a_list[i]=a_list[i],a_list[0] 31 return (i+1,a_list[:i],a_list[i+1:]) 32 33 34 def mid_select(a_list): 35 if len(a_list)>5: 36 k=(len(a_list)+1)//2 37 a_list_next=a_list[:] 38 base=0 39 while a_list_next !=[]: 40 a_list_mid =[] 41 for i in range(len(a_list_next)//5): 42 a_list_part=a_list_next[5*i:5*(i+1)] 43 a_list_order=insertion_sort(a_list_part) 44 mid_part=a_list_order[2] 45 a_list_mid.append(mid_part) 46 if len(a_list_next)%5 !=0: 47 a_list_part=a_list_next[5*(len(a_list_next)//5):] 48 a_list_order=insertion_sort(a_list_part) 49 mid_part =a_list_order[(len(a_list_order)+1)//2-1] 50 a_list_mid.append(mid_part) 51 mid= mid_select(a_list_mid) 52 i,a_list_left,a_list_right = partition(a_list_next,mid) 53 54 i =base +i # revise the index of mid in the original a_list 55 if i==k: 56 return mid 57 elif i <k: 58 a_list_next=a_list_right 59 base =i 60 else: 61 a_list_next=a_list_left 62 return False 63 else: 64 a_list_order=insertion_sort(a_list) 65 return a_list_order[(len(a_list)+1)//2-1] 66 67 68 a_list=[2,54,26,4,13,5,8,14,7,93,6,17,77,31,44,55,20,26,93] 69 70 print (mid_select(a_list)) 71
算法導論第二版書的習題9.3-8也用到了相似的思想。下分析。
EX 9.3-8 設x[1..n]和Y[1..n]爲兩個數組,每一個都包含n個已排好序的數。給出一個求數組X和Y中全部2n個元素的中位數的O(logn)時間的算法。
看到O(logn)時間複雜度,首先想到分治思想的T(n)=T(n/2)+O(1)模式,那麼如何才能經過常數次比較(最好一次),直接排除一部分不多是中位數的元素呢(這部分元素的數量與n成線性關係便可)?首先最直觀的考慮這部分元素數量爲n/2,因而想到比較X和Y的中位數(偶數指下中位數),索引爲mid,若是X[mid]=Y[mid],則X[mid](Y[mid])就是該2n個元素的中位數;若是X[mid]<Y[mid],則若將X和Y這2n個元素排序,Y[mid]必然排在第n個元素以後(反證法可得),同理 X[mid]必然排在前n個元素,所以 X[mid]以前的元素和Y[mid]以後的元素能夠直接扔掉,由於它們確定不是中位數。因而保留一半的元素遞歸調用自身。算法思路理清。
可是還有一個問題,當n爲偶數時,取的下中位數進行比較,若兩者不等,則下一次處理的兩數組長度不等,遇到困難。所以n爲偶數時,我對捨棄後一半元素的數組保留至其中位數的後一位,保證了算法的正常遞歸。代碼以下。
1 import random 2 def mid_array_938(list_a,list_b): 3 if len(list_a) >2: 4 len_mid=(len(list_a)+1)//2 5 mid_a= list_a[len_mid-1] 6 mid_b= list_b[len_mid-1] 7 if mid_a == mid_b: 8 return mid_a 9 elif mid_a< mid_b: 10 if len(list_a)%2==0: 11 return mid_array_938(list_a[len_mid-1:],list_b[:len_mid+1]) 12 else: 13 return mid_array_938(list_a[len_mid-1:],list_b[:len_mid]) 14 else: 15 if len(list_a)%2==0: 16 return mid_array_938(list_a[:len_mid+1],list_b[len_mid-1:]) 17 else: 18 return mid_array_938(list_a[:len_mid],list_b[len_mid-1:]) 19 else: 20 list_ab=list_a + list_b 21 list_ab=insertion_sort(list_ab) 22 return list_ab[(len(list_ab)+1)//2-1] 23 24 25 def insertion_sort(a_list): 26 last=len(a_list)-1 27 if last>0: 28 list1=insertion_sort(a_list[:last]) 29 i=len(list1)-1 30 temp=a_list[last] 31 while i>=0: 32 if temp<=list1[i]: 33 i-=1 34 else: 35 break 36 for pos in range(last,i+1,-1): 37 a_list[pos]=list1[pos-1] 38 a_list[i+1]=temp 39 a_list[:i+1]=list1[:i+1] 40 return a_list 41 42 測試: 43 list_a=[1,4,6,9,13,19,35,52] 44 list_b=[2,3,15,18,19,20,43,45] 45 print mid_array_938(list_a,list_b) 46 輸出: 47 15