''' 快速排序 分类 算法 快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要 Ο(nlogn) 次比较。 在最坏状况下则需要 Ο(n2) 次比较,但这种状况并不常见。事实上,快速排序通常明显比其他 Ο(nlogn) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。 快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)。 快速排序又是一种分而治之思想在排序算法上的典型应用。本质上来看,快速排序应该算是在冒泡排序基础上的递归分治法。 快速排序的名字起的是简单粗暴,因为一听到这个名字你就知道它存在的意义,就是快,而且效率高! 它是处理大数据最快的排序算法之一了。虽然 Worst Case 的时间复杂度达到了 O(n²),但是人家就是优秀, 在大多数情况下都比平均时间复杂度为 O(n logn) 的排序算法表现要更好,可是这是为什么呢,我也不知道。 好在我的强迫症又犯了,查了 N 多资料终于在《算法艺术与信息学竞赛》上找到了满意的答案: 快速排序的最坏运行情况是 O(n²),比如说顺序数列的快排。但它的平摊期望时间是 O(nlogn),且 O(nlogn) 记号中隐含的常数因子很小,比复杂度稳定等于 O(nlogn) 的归并排序要小很多。 所以,对绝大多数顺序性较弱的随机数列而言,快速排序总是优于归并排序。 1. 算法步骤 1从数列中挑出一个元素,称为 "基准"(pivot); 2重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。 在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作; 3递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序; 快速排序的基本思想 快速排序是对冒泡排序的一种改进。 它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小, 然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。 快速排序的算法分析 快速排序算法通过多次比较和交换来实现排序,其排序流程如下: 1首先设定一个分界值,通过该分界值将数组分成左右两部分。 2将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时左边部分中各元素都小于或等于分界值, 而右边部分中各元素都大于或等于分界值。 3然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边 放置较小的值,右边放置较大值。右侧的数组数据也可以做类似处理。 4重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两部分各 数据排序完成后,整个数组的排序也就完成了。 快速排序的排序步骤 设要排序的数组是A[0]....A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数 都放到它的左边,所有比它大的数都放到它右边,这个过程称为一趟快速排序 值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。 一趟快速排序的算法是: 1设置二个变量i,j,排序开始的时候:i=0,j=n-1; 2以第一个数组元素作为关键数据,赋值给key,即key=A[0]; 3从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]的值交换; 4从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的值A[i],将A[i]和A[j]的值交换; 5重复3,4步,直到i=j;(3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中的A[i]不大于key的时候改变j,i的值, 使得j=j-1,i=i+1,直到找到为止。找到符合条件的值,进行交换的时候i,j指针位置不变。另外,i==j这一过程一定正好是 i+或j-完成的时候,此时循环结束)。 例: 假设一开始序列a = [5,3,7,6,4,1,0,2,9,10,8] 1 ref = 5,i=0,j=10,从后向前找,a[7]=2 < 5,此时i=0,j=7,得到a=[2,3,7,6,4,1,0,5,9,10,8] 2 ref = 5,i=0,j=7,从前向后找,a[2]=7 > 5,此时i=2,j=7,得到a=[2,3,5,6,4,1,0,7,9,10,8] 3 ref = 5,i=2,j=7,从后向前找,a[6]=0 < 5,此时i=2,j=6,得到a=[2,3,0,6,4,1,5,7,9,10,8] 4 ref = 5,i=2,j=6,从前向后找,a[3]=6 > 5,此时i=3,j=6,得到a=[2,3,0,5,4,1,6,7,9,10,8] 5 ref = 5,i=3,j=6,从后向前找,a[5]=1 < 5,此时i=3,j=5,得到a=[2,3,0,1,4,5,6,7,9,10,8] 6 ref = 5,i=3,j=5,从前向后找,a[6]=6 > 5,(正常区间已经无法满足的数),这时,i=j=6,ref成为 一条分界线,它之前的数都比它小,之后的数都比它大,对于前后两部分数,可以采用同样的方法来排序。 方法1: def quickSort(arr, left=None, right=None): left = 0 if not isinstance(left,(int, float)) else left right = len(arr)-1 if not isinstance(right,(int, float)) else right if left < right: partitionIndex = partition(arr, left, right) quickSort(arr, left, partitionIndex-1) quickSort(arr, partitionIndex+1, right) return arr def partition(arr, left, right): pivot = left index = pivot+1 i = index while i <= right: if arr[i] < arr[pivot]: swap(arr, i, index) index+=1 i+=1 swap(arr,pivot,index-1) return index-1 def swap(arr, i, j): arr[i], arr[j] = arr[j], arr[i] 方法2: # def quickSort(l, p, r): #p为最最左边的索引号即i=0,r为最右边的索引号即j=n-1 # if p < r: #当p<r,表示没有超出区间范围 # q = partion(l,p,r) # print(q) # quickSort(l,p,q) # quickSort(l,q+1,r) # # def partion(l,p,r): # i=p-1 #初始值设为-1 # for j in range(p,r): #p为最左边的 # if l[j]<=l[r]: #如果这个列表中有值小于等于最后一个值 # i+=1 #i向右移一位 # print(i,j) # l[i],l[j]=l[j],l[i] # # print('i:',i) # l[i+1], l[r] = l[r], l[i+1] # return i # # l = [5,3,7,6,4,1,11,0,2,9,10,13,12,8] # quickSort(l,0,len(l)-1) # print(l) 方法3: 假设有n项数据,数据值用k1,k2,k3,......kn来表示。 1先在数据中假设一个虚拟中间值K(为了方便,一般取第一个位置上的数)。 2从左向右查数据ki,使得ki>K,ki的位置数记为i。 3从右向左查数据kj,使得kj<K,kj的位置数记为j。 4若i<j,那么数据ki,kj交换位置,并回到2,3步骤。 5若i>=j,那么数据kj,K交换位置,并以j为基准点分割成左右两部分,然后针对左右两部分再 进行1-5步骤,直到左半边数据等于右半边数据为止。 快速排序的结果有两种形式,递增和递减数列。 例: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 6 1 2 7 9 3 4 5 10 8 步骤1:取原始值的第一个数6为虚拟中间值,K=6,然后从左向右找大于6的值,即k4=7,再从右向左 找小于6的值,即K8=5. 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 6 1 2 7 9 3 4 5 10 8 K i j 根据第4条法则,i<j时,交换ki,kj,得到如下列数: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 6 1 2 5 9 3 4 7 10 8 步骤2:重复2,3法则,继续从左向右找大于6的值,即k5=9,再从右向左 找小于6的值,即K7=4. 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 6 1 2 5 9 3 4 7 10 8 k i j 根据第4条法则,i<j时,交换ki,kj,得到如下列数: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 6 1 2 5 4 3 9 7 10 8 步骤3,重复2,3法则,继续从左向右找大于6的值,即k7=9,再从右向左 找小于6的值,即K6=3. 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 6 1 2 5 4 3 9 7 10 8 k j i 根据第5条法则,i>=j时,交换kj,k,得到如下列数: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 3 1 2 5 4 6 9 7 10 8 步骤4,K=6是真正的中间值,左边的部分都小于6,右边的部分都大于6. 步骤5,对中间值6左边的数据进行排序,按照快速度排序法则,在左半边部分取K=3, 从左边到右找大于3的值,i=4,k4=5,从右边到左找小于3的值,j=3,K3=2 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 3 1 2 5 4 不处理 K j i 根据第5条法则,i>=j时,交换kj,k,得到如下列数: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 2 1 3 5 4 不处理 步骤6,交换K值后,k=3成了新的中间值,把左边的部分分成新的左右二部分,小于3的都在左边 大于3的都在右边。 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 2 1 3 5 4 不处理 步骤7,接下来对中间值3的左,右两侧排序,经过排序之后,最终排序结果如下 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 1 2 3 4 5 不处理 步骤8:此时,左边已经排好,再排右边,k=9,从左到右找大于9的值,i=9,k9=10,从右到 到左找小于9的值,j=10,k10=8, 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 已经处理好 9 7 10 8 k i j 根据第4条法则,i<j时,交换ki,kj,得到如下列数: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 已经处理好 9 7 8 10 步骤9,k=9,从左到右找大于k的值,i=10,k10=10,从右到左找比9小的值, j=9,k9=8, 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 已经处理好 9 7 8 10 k j i 根据第5条法则,i>=j时,交换kj,k,得到如下列数: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 已经处理好 8 7 9 10 步骤10,k=8,从左到右找大于k的值,i=9,k9=9,从右到左找比8小的值, j=8,k8=7, 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 已经处理好 8 7 9 10 k j i 根据第5条法则,i>=j时,交换kj,k,得到如下列数: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 已经处理好 7 8 9 10 步骤11,左右二边都已排好,最后得到: 位1 位2 位3 位4 位5 位6 位7 位8 位9 位10 1 2 3 4 5 6 7 8 9 10 至此快速排序结束。 ''' def kspx(l,start,end): if start > end: return k = l[start] i,j=start,end while True: while j > i and l[j]>=k: j -= 1 while i<j and l[i]<=k: i += 1 if i < j: l[i],l[j]=l[j],l[i] elif i >= j: l[start],l[j]=l[j],l[start] break kspx(l,start,j-1) kspx(l,j+1,end) import random as r a = [] for i in range(50): v = r.randint(0,100) a.append(v) v = kspx(a,0,len(a)-1) print(a)
5快速排序
最新推荐文章于 2024-06-16 21:14:55 发布