对于包含n个数的输入数组来说,快速排序是一种最快情况时间复杂度为O(n^2)的排序算法。虽然最坏情况时间复杂度很差,但是快速排序通常是实际排序应用中最好的选择,因为它的平均性能非常好,期望是O(nlgn),而且 O(nlgn) 中隐含的常数因子非常小,并且具有原址性。

1.快速排序的描述

快速排序可以理解为选择一个基准,以这个基准将列表分为大于基准和小于基准的两部分,并类似二分法继续向下选择基准。

每一趟快速排序之后都有一个元素位于正确的位置。

简单的描述就是:从前向后找比基准大的并交换,从后向前找比基准小的并交换。

以4为基准的一趟排序

代码,时间复杂度O(n):

def quick_sort(self, init_list, i):
    """
    快速排序
    :param init_list: 排序列表
    :param i: 比较元素索引
    :return:
    """
    length = len(init_list)
    # 计算左右索引
    left_index = 0
    right_index = length - 1
    # 将比较元素放到最后,防止它是最大值时不进行交换
    temp = init_list[i]
    init_list[i] = init_list[right_index]
    init_list[right_index] = temp
    # 比较元素值
    flag = init_list[right_index]
    # 比较元素索引(因为交换元素位置会变)
    flag_index = right_index
    # 重复执行直至左右标记重叠
    while left_index != right_index:
        # 从左向右找比flag大的数交换
        for i in range(left_index, right_index):
            if flag < init_list[left_index]:
                temp = flag
                init_list[flag_index] = init_list[left_index]
                flag_index = i
                init_list[left_index] = temp
                break
            else:
                left_index += 1
        # 从右向左找比flag小的数交换
        for i in range(right_index, left_index, -1):
            if flag > init_list[right_index]:
                temp = flag
                init_list[flag_index] = init_list[right_index]
                flag_index = i
                init_list[right_index] = temp
                break
            else:
                right_index -= 1
    return init_list

2.快速排序的性能

最坏情况划分

考虑一种情况:每次选择的基准都是当前序列中最小的序列,因此需要进行n次排序,每次排序代价是 O(n) ,因此最坏的时间复杂度是 O(n^2)。

最好情况划分

考虑一种情况:每次选择的基准都是当前序列中中间的数字,每次选择的基准正好将序列分成了一棵满二叉树,因此最好情况的时间复杂度为 O(nlgn)。

平衡的划分

即使是没有达到完美的1/2分,加入是9:1的区分,依然可以达到一个 O(nlgn)的时间复杂度。

3.快速排序的随机化版本

随机化的版本不再固定的选择某一个数,而是随机选择向下递归,因此我们需要稍微修改一下之前的快排代码,添加一个左右指针,指定本地递归需要排序的范围。

def quick_sort_for_random(self, init_list, i, left, right):
"""
快速排序
:param init_list: 排序列表
:param i: 比较元素索引
:return:
"""
length = len(init_list)
# 计算左右索引
left_index = left
right_index = right
# 将比较元素放到最后,防止它是最大值时不进行交换
temp = init_list[i]
init_list[i] = init_list[right_index]
init_list[right_index] = temp
# 比较元素值
flag = init_list[right_index]
# 比较元素索引(因为交换元素位置会变)
flag_index = right_index
# 重复执行直至左右标记重叠
while left_index != right_index:
# 从左向右找比flag大的数交换
for i in range(left_index, right_index):
if flag < init_list[left_index]:
temp = flag
init_list[flag_index] = init_list[left_index]
flag_index = i
init_list[left_index] = temp
break
else:
left_index += 1
# 从右向左找比flag小的数交换
for i in range(right_index, left_index, -1):
if flag > init_list[right_index]:
temp = flag
init_list[flag_index] = init_list[right_index]
flag_index = i
init_list[right_index] = temp
break
else:
right_index -= 1
return init_list, left_index

然后是随机快排:

def random_quick_sort(self, init_list, left, right):

"""
随机快速排序
:param init_list:列表
:return:
"""
# 如果是单一元素排序,直接返回
if right == left:
return init_list
# 随机初始化一个目标元素
random_index = random.randint(left, right)
# 进行排序,获取到排序后的列表以及目标元素现在的索引
sort_list, sort_flag_index = self.quick_sort_for_random(init_list.copy(), random_index, left, right)
# 如果当前目标不是最小值,则排序左子树
if sort_flag_index != left:
sort_list = self.random_quick_sort(sort_list, left, sort_flag_index - 1)
# 如果当前目标不是最大值,排序右子树
if sort_flag_index != right:
sort_list = self.random_quick_sort(sort_list, sort_flag_index + 1, right)
return sort_list

运行结果:


0 条评论

发表回复

Avatar placeholder

您的邮箱地址不会被公开。 必填项已用 * 标注