快速排序和快速排序优化运行结果对比(处理不同容量大小的数组)

分别实现快速排序和优化的快速排序算法(子问题规模小于某个阈值时使用直接插入排序替换),并按如下要求进行测试:测试数据的规模分别为 100,200,300,⋯,10000) 共100组,测试数据分为正序、逆序和随机三种序列分布。对上述测试要求下的两种快速排序算法执行的时间趋势变化以图形的方式展示。

1、代码

import random
import time
import matplotlib.pyplot as plt

#生成数组的函数,给两个参数,一个是数组大小,一个是排序方式
def generate_data(size, order='random'):
    if order == 'ascending':
        return list(range(size))#升序
    elif order == 'descending':
        return list(range(size, 0, -1))#降序
    else:
        return [random.randint(0, size * 10) for _ in range(size)]#利用random.randint生成随机数组

#算法[q_s,o_q_s]、数组大小[100*i for i in range(1,101)]、数组顺序([ascending,descending,random])
#下面这个函数对不同算法、不同数组大小、数组不同顺序的运行时间进行计算,存放在了times{}的字典里:
#times={'ascending': [(100, 2),(200, 2), (300, 2),.....(10000,2)],'descending':[]....}
def test_algorithm(algorithm, sizes, orders):
    times = {}
    for size in sizes:
        for order in orders:
            data = generate_data(size, order)
            start_time = time.time()
            algorithm(data)
            end_time = time.time()
            execution_time = end_time - start_time
            times.setdefault(order, []).append((size, execution_time))
    return times

#快速排序,由于双指针法出现了栈溢出,这里采用另一种快排法,依旧是分而治之的思想,这种方法更简洁易懂
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)
#插入排序算法
def insertion_sort(arr):
    for i in range(1,len(arr)): #从1开始遍历是因为假设第一个元素是已经排好序的
        j=i
        while j>0 and arr[j]<arr[j-1]:      #因为待排序元素arr[j]在交换位置后,序号减一
            (arr[j-1],arr[j])=(arr[j],arr[j-1])  #所以当它是最小即0,
            j-=1
    return arr
#优化的快速排序法,当递归数组长度到达一定阈值时,采用直接插入排序
def optimized_quicksort(arr, threshold=10):
    if len(arr) <= threshold:
        insertion_sort(arr)
        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 optimized_quicksort(left, threshold) + middle + optimized_quicksort(right, threshold)
#三路法
def partition3(arr, low, high):
    lt = low
    gt = high
    i = low + 1
    pivot = arr[low]
    while i <= gt:
        if arr[i] < pivot:
            arr[i], arr[lt] = arr[lt], arr[i]
            lt += 1
            i += 1
        elif arr[i] > pivot:
            arr[i], arr[gt] = arr[gt], arr[i]
            gt -= 1
        else:
            i += 1
    return lt, gt

def quicksort3(arr, low, high):
    if low < high:
        lt, gt = partition3(arr, low, high)
        quicksort3(arr, low, lt - 1)
        quicksort3(arr, gt + 1, high)

def quicksort3_wrapper(arr):
    quicksort3(arr, 0, len(arr) - 1)
    return arr

def find_kth_smallest(arr, k):
    if len(arr) == 1:
        return arr[0]
    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]
    if k <= len(left):
        return find_kth_smallest(left, k)
    elif k <= len(left) + len(middle):
        return middle[0]  # 假设k值正确,middle中至少有一个元素
    else:
        return find_kth_smallest(right, k - len(left) - len(middle))

# 设定测试规模和数据类型
sizes = [100 * i for i in range(1, 101)]
orders = ['ascending', 'descending', 'random']

# 测试快速排序和优化的快速排序
quicksort_times = test_algorithm(quicksort, sizes, orders)
optimized_quicksort_times = test_algorithm(optimized_quicksort, sizes, orders)

# 测试三路划分快速排序
# quicksort3_times = test_algorithm(quicksort3_wrapper, sizes, orders)

# 绘制时间趋势图
plt.figure(figsize=(12, 8))

for order, times in quicksort_times.items():
    sizes, execution_times = zip(*times)
    plt.plot(sizes, execution_times, label=f'Quicksort:({order})')

for order, times in optimized_quicksort_times.items():
    sizes, execution_times = zip(*times)
    plt.plot(sizes, execution_times, label=f'Optimized Quicksort:({order})')

# for order, times in quicksort3_times.items():
#     sizes, execution_times = zip(*times)
#     plt.plot(sizes, execution_times, label=f'Quicksort3 {order}')

plt.xlabel('Data Size')
plt.ylabel('Execution Time (seconds)')
plt.title('Execution Time Trends for Different Sorting Algorithms')
plt.legend()
plt.grid(True)
plt.show()

2、运行结果及结论

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、时间复杂度分析

基本情况:如果数组只有一个元素,那么直接返回该元素,时间复杂度为O(1)。

划分过程:代码首先选择了一个基准值(pivot),然后将数组划分为三部分:小于基准值的元素(left),等于基准值的元素(middle),和大于基准值的元素(right)。这个划分过程的时间复杂度是O(n),其中n是数组的长度,因为我们需要遍历整个数组来进行划分。
递归调用:
如果k小于或等于left的长度,那么我们递归地在left中寻找第k小的元素。
如果k大于left的长度但小于或等于left和middle的长度之和,那么我们知道第k小的元素就是middle中的第一个元素(假设k值正确,middle中至少有一个元素)。
否则,我们在right中寻找第(k - len(left) - len(middle))小的元素。
在每次递归调用中,我们排除了至少一部分数组(left、middle或right),因此每次递归调用都使得问题规模减小。在最坏情况下,每次划分可能都不均衡,导致每次递归调用都只排除了一个元素。然而,由于每次递归调用都伴随着一次O(n)的划分过程,因此总的时间复杂度仍然是O(n^2)。
然而,在平均情况下,每次划分都能将数组大致均等地划分为两部分,这导致递归的深度为O(log n),并且每次递归都伴随着一次O(n)的划分过程。因此,平均情况下的时间复杂度是O(n log n)。
请注意,这里的O(n log n)是在假设pivot选择是随机的或者能够平均分布的情况下得出的。在实际应用中,如果pivot选择不当(例如,总是选择数组的第一个或最后一个元素作为pivot),那么最坏情况的时间复杂度O(n^2)可能会更常见。
综上所述:
这段代码的时间复杂度在最坏情况下是O(n^2),在平均情况下是O(n logn)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值