标签:期望 核心 复杂 set 递归调用 range ret 技术 长度
初学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]
2、选择排序( selection sort)
该算法依次找出最大、次大……的元素并置于应属的位置,与冒泡排序的实现效果相同,只不过选择排序只是在每一轮选择结束后交换一次元素,减少了交换元素的次数。但比较次数没有减少,故时间复杂度依然是O(n^2)。
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]
3、插入排序(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]
把待排序数组分成两部分分别排序,以递归调用自身实现,对已排好的两部分比较合并。
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]
6、快速排序(quick sort)
采用分治思想,每次选取list中的第一个元素作为主元(pivot),通过比较找到其应属的位置,将原list划分为左右两个list,分别递归调用自身。
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])
7、计数排序(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)
8、基数排序(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)
9、桶排序(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?个桶中。
10、期望线性时间的第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
11、最坏情况线性时间的中位数选择
随机化算法可以获得良好的期望时间复杂度,却对最坏情况无能为力。该求取中位数(偶数指下中位数)的算法优化了主元的选择方法,将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
标签:期望 核心 复杂 set 递归调用 range ret 技术 长度
原文地址:http://www.cnblogs.com/seven-wong/p/6079503.html