数据结构与算法----归并排序与快速排序(Python版)

一、归并排序 Merge Sort
下面我们来看看 分治策略 在排序中的应用
归并排序是递归算法,思路是将数据表持 续分裂为两半,对两半分别进行归并排序
递归的 基本结束条件 是:数据表仅有 1 个数据项 ,自然是排好序的;
缩小规模 :将数据表分裂为相等的两半,规模减 为原来的二分之一;
调用自身 :将两半分别调用自身排序,然后将分 别排好序的两半进行归并,得到排好序的数据表
数据结构与算法----归并排序与快速排序(Python版)_第1张图片
源码:
def mergeSort(alist):
    if len(alist) > 1:
        mid = len(alist)//2
        lefthalf = alist[:mid]
        righthalf = alist[mid:]

        mergeSort(lefthalf) #左排序
        mergeSort(righthalf) #右排序

        i=j=k=0
        #拉链式交错把左右两边从小到大归并到结果列表中
        while i  i:
            alist[k] = lefthalf[i]
            i = i+1
            k = k+1
        #归并右半部分剩余项
        while len(righthalf) > j:
            alist[k] = righthalf[j]
            j = j+1
            k = k+1
    return alist

testlist =  [1,2,6,4,8,5,7,3]
print(mergeSort(testlist))
#归并排序优化
def merge_sort(alist):
    if len(alist) > 1:
        mid = len(alist)//2
        left_half = alist[:mid]
        right_half = alist[mid:]

        left_half = merge_sort(left_half)
        right_half = merge_sort(right_half)

        merge_list = []
        while left_half and right_half:
            if left_half[0] <= right_half[0]:
                merge_list.append(left_half.pop(0))
            else:
                merge_list.append(right_half.pop(0))
        
        merge_list.extend(left_half if left_half else right_half)
        return merge_list
    else:
        return alist
    

testlist =  [1,2,6,4,8,5,7,3]
print(merge_sort(testlist))

 

归并排序:算法分析
将归并排序分为两个过程来分析: 分裂 归并
分裂的过程,借鉴二分查找中的分析结果 ,是对数复杂度,时间复杂度为 O(log n)
归并的过程,相对于分裂的每个部分,其 所有数据项都会被比较和放置一次,所以 是线性复杂度,其时间复杂度是 O(n)
综合考虑,每次分裂的部分都进行一次 O(n) 的数
据项归并,总的时间复杂度是O(nlog n)
 
最后,我们还是注意到两个切片操作
为了时间复杂度分析精确起见, 可以通过 取消切片 操作,改为传递两个分裂部分 的起始点和终止点,也是没问题的, 只是算法可读性稍微牺牲一点点。
我们注意到归并排序算法使用了 额外 1 的存储空间用于归并
这个特性在对特大数据集进行排序的时候 要考虑进去
 
 
二、快速排序算法及分析
快速排序的思路是依据一个 中值 数据 项来把数据表分为 两半 :小于中值的一半 和大于中值的一半,然后每部分分别进行 快速排序(递归)
如果希望这两半拥有相等数量的数据项,则应该 找到数据表的“中位数” 但找中位数需要计算开销!要想没有开销,只能 随意找一个数来充当“中值” 比如,第 1 个数。
快速排序的递归算法 递归三要素 如下
基本结束条件 :数据表仅有 1 个数据项, 自然是排好序的
缩小规模 :根据 中值 ,将数据表分为 两半,最好情况是相等规模的两半
调用自身 :将两半分别调用自身进行排序 (排序基本操作在分裂过程中)
分裂数据表的目标:找到 中值 的位置
分裂数据表的手段 设置左右标( left/rightmark
左标向右移动,右标向左移动
左标一直向右移动,碰到比中值大的就停止
右标一直向左移动,碰到比中值小的就停止
然后把左右标所指的数据项 交换
继续移动,直到左标移到右标的右侧,停止移动 这时右标所指位置就是“中值”应处的位置 将中值和这个位置交换 分裂完成,左半部比中值小,右半部比中值大
数据结构与算法----归并排序与快速排序(Python版)_第2张图片
 
源码为:
数据结构与算法----归并排序与快速排序(Python版)_第3张图片
数据结构与算法----归并排序与快速排序(Python版)_第4张图片
 
快速排序:算法分析
快速排序过程分为两部分: 分裂 移动
如果分裂 总能 把数据表分为 相等 两部分 ,那么 就是 O(log n) 的复杂度;
而移动需要将每项都与中值进行比对,还是 O(n)
综合起来就是 O(nlog n)
而且,算法运行过程中不需要额外的存储 空间。
但是,如果不那么幸运的话,中值所在的 分裂点过于偏离中部,造成左右两部分数 量不平衡
极端情况,有一部分始终没有数据 ,这样 时间复杂度就退化到 O(n 2 )
还要加上递归调用的开销(比冒泡排序还糟糕)
可以适当改进下 中值 的选取方法,让中值 更具有代表性
比如“三点取样”,从数据表的头、尾、中间选 出中值 会产生额外计算开销,仍然不能排除极端情况

 

 

你可能感兴趣的:(数据结构与算法)