################## 快速排序 #######################
""" 快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。 步骤为: 1,挑选基准值:从数列中挑出一个元素,称为"基准"(pivot); 2,分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。 在这个分割结束之后,对基准值的排序就已经完成; 3,递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。 递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。 """
################## 快速排序 #######################
""" alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] 第一种实现 通过两个游标,left,right ,把第一个元素作为基准值, 第一轮,54作为基准值,left游标指向26,right游标指向20 退出条件是low<=right left指向的都要比54小,否则就停住, right指向的都要比54大,否则就停住, 然后左右交换,继续下去,直到满足退出条件,第一轮结束, 第二轮,继续执行相同的操作,递归执行, 一直递归结束,进行合并 """
代码:
def quick_sort(alist, first, last): """快速排序""" if first >= last: # 递归的退出条件 return mid_value = alist[first] low = first # high = last while low < high: # 记住是交替执行的,两个游标重合的时候就是退出的时候, while low < high and alist[high] >= mid_value: high -= 1 alist[low] = alist[high] while low < high and alist[low] < mid_value: low += 1 alist[high] = alist[low] # 循环退出之后,low和high是相等的, alist[low] = mid_value # 将基准元素放到该位置, # 对基准元素左边的子序列进行快速排序 quick_sort(alist, first, low - 1) # first :0 low -1 原基准元素靠左边一位 # 对基准元素右边的子序列进行快速排序 quick_sort(alist, low + 1, last) # low+1 : 原基准元素靠右一位 last: 最后 if __name__ == ‘__main__‘: alist = [54, 26, 93, 17, 77, 31, 44, 55, 20] quick_sort(alist, 0, len(alist) - 1) print(alist)
最优时间复杂度分析
""" 看代码不好分析了,看逻辑 1,第一轮复杂度就是n, 2,第二轮复杂度也是n, 3,多少轮结束?要取对数,logn, 最优时间复杂度:总的就是O(nlogn),最好的情况就是每一个值都是在中间, 最坏时间复杂度,O(n^2) """
################## 快速排序 #######################
# 第二个版本 # 这个方法是算法导论里面的,比较上一种方法,在于分片的过程不同,只用了一次循环,并且一趟就完成了分片,比较之下,代码要简洁的多, # 这个函数是分成两部,返回一个分割下标,这个下标就是把大于基准值的放到一边,小于基准值的放到一边, # arr = [10, 7, 8, 9, 2, 1, 5] def partition(arr, low, high): i = (low - 1) # 最小元素索引 pivot = arr[high] # 把最右边最为一个分割点, for j in range(low, high): # 当前元素小于或等于 pivot if arr[j] <= pivot: # 遍历每一个列表元素和基准值比较 i = i + 1 # 第一次触发就是变成0,表示第一个元素是最小值, arr[i], arr[j] = arr[j], arr[i] # 因为上一步表示了第一个元素是最小值所以要和 i 替换, # 上面循环退出的时候:i =2 列表: [2,1,8,9,10,7,5] arr[i + 1], arr[high] = arr[high], arr[i + 1] # high = 5 这个时候high就是所谓的基准值 i+1 就是8了, # 替换之后,就是基准值的左边都小于基准值,右边都大于基准值了, return (i + 1) # 返回这个i+1 就是所谓的基准值分割点的索引了, # arr[] --> 排序数组 # low --> 起始索引 # high --> 结束索引 # 快速排序函数 def quickSort(arr, low, high): if low < high: pi = partition(arr, low, high) quickSort(arr, low, pi - 1) quickSort(arr, pi + 1, high) arr = [10, 7, 8, 9, 2, 1, 5] n = len(arr) quickSort(arr, 0, n - 1) print("排序后的数组:") for i in range(n): print("%d" % arr[i])
################## 快速排序 #######################
""" 先来看一个 我更想称之为伪快排的快排代码: 这段代码最关键的是pivot这个参数,这段代码里取序列的第一个元素,然后以这个元素为分组的基准,利用列表解析式使得它左边的值都比它小,右边的值都比它大。然后再分别对这些序列进行递归排序。
def quicksort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quicksort(left) + middle + quicksort(right) print(quicksort([3, 6, 8, 19, 1, 5])) # [1,3, 5, 6, 8, 19]
def quick_sort(b): """快速排序""" if len(b) < 2: return arr # 选取基准,随便选哪个都可以,选中间的便于理解 mid = arr[len(b) // 2] # 定义基准值左右两个数列 left, right = [], [] # 从原始数组中移除基准值 b.remove(mid) for item in b: # 大于基准值放右边 if item >= mid: right.append(item) else: # 小于基准值放左边 left.append(item) # 使用迭代进行比较 return quick_sort(left) + [mid] + quick_sort(right)
这段代码虽然短小利于理解,但是其效率很低,主要体现在以下方面: 分组基准的选取过于随便,不一定可以取到列表的中间值 空间复杂度大,使用了两个列表解析式,而且每次选取进行比较时需要遍历整个序列。 若序列长度过于小(比如只有几个元素),快排效率就不如插入排序了。 递归影响性能,最好进行优化。 """
################## 快速排序 #######################
""" 特点 稳定性:快排是一种不稳定排序,比如基准值的前后都存在与基准值相同的元素,那么相同值就会被放在一边,这样就打乱了之前的相对顺序 比较性:因为排序时元素之间需要比较,所以是比较排序 时间复杂度:快排的时间复杂度为O(nlogn) 空间复杂度:排序时需要另外申请空间,并且随着数列规模增大而增大,其复杂度为:O(nlogn) 归并排序与快排 :归并排序与快排两种排序思想都是分而治之,但是它们分解和合并的策略不一样:归并是从中间直接将数列分成两个,而快排是比较后将小的放左边大的放右边,所以在合并的时候归并排序还是需要将两个数列重新再次排序,而快排则是直接合并不再需要排序,所以快排比归并排序更高效一些,可以从示意图中比较二者之间的区别。 快速排序有一个缺点就是对于小规模的数据集性能不是很好。 """
################## 快速排序 #######################
################## 快速排序 #######################
原文地址:https://www.cnblogs.com/andy0816/p/12348378.html
时间: 2024-10-12 16:00:57