Python实现十大经典排序 I

Python实现十大经典排序 I

摘要: 本博客主要介绍冒泡排序、选择排序、插入排序、快速排序、归并排序等五种排序方法思想及Python实现。

文档版本
版本号更新时间作者更新章节更新概述
V1.02019-01-14蒙蕤1-5算法描述、Python实现
V1.12019-01-16蒙蕤1-5

代码注释 


1.冒泡排序

在排序算法中,冒泡排序相对容易理解和编码。其策略是从列表的开头处开始,比较一对数据项,直到移动到列表的末尾。每当成对的两项之间顺序不正确时,交换其位置。这个过程的效果就是将最大的项以冒泡的方式排序到列表的末尾。然后,算法从列表开头到倒数第2项重复这一过程,依次类推,直到该算法从列表的最后一项执行。此时列表已排序好。以下用表格展示这一过程,示例用5个元素的列表:

未排序列表第一次比较第二次比较第三次比较第四次比较
51*111
15*2*22
225*4*4
4445*3*
33335*

 

冒泡排序存在循环嵌套,对于大小为n的列表,内部的循环执行\frac{1}{2}n^{2}-\frac{1}{2}n次,其复杂度为O(n^{2})。

冒泡排序的Python实现:

def bubble_sort(lyst):
    n = len(lyst)
    while n > 1:
        # swapped 标记在本次 while 循环中是否出现元素交换
        # 如果没有交换,则说明列表已经排序好,直接return即可
        # 优化最坏的情况复杂度
        swapped = False    
        i = 1   # 开始位置,每次都从第一个元素往后比较
        while i < n:    # 
            if lyst[i] < lyst[i-1]:
                swapped = True
                lyst[i], lyst[i-1] = lyst[i-1], lyst[i]
            i += 1
        if not swapped:
            return    # no swaps so that lyst sorted
        n -= 1    # 最大项已放末尾,下次循环只需比较该位置之前元素

2.选择排序

顾名思义,从列表中找到最小项,如果其位置不是列表的第一项,则交换两个位置的项。然后算法从第二个位置开始执行该过程,直到算法到达整个过程的最后一项。以下用表格展示该过程:

 

选择排序的时间复杂度与冒泡排序一样。

选择排序的Python实现:

def select_sort(lyst):
    for i in range(0, len(lyst)-1):
        min_index = i       # 最小元素下标,先定义待排序位置最小
        for j in range(i, len(lyst)):
            if lyst[j] < lyst[min_index]:
                min_index = j    # 更新最小元素下标
        if min_index != i:   # 交换需排序位置元素及最小元素
            lyst[min_index], lyst[i] = lyst[i], lyst[min_index]

3. 插入排序

插入排序的策略如下:

在第 i 轮通过列表的时候(i 为 1 到 n - 1),第  i 个项应该插入到列表的前 i 个项之中的正确位置。

1.第 i 轮之后,前 i 个项应该是排序好的。

2.其时间复杂度依然是O(n^{2})。

插入排序的Python实现:

def insertion_sort(lyst):
    # 从第2个元素开始循环,之前只有一个元素,肯定顺序正确
    for i in range(1, len(lyst)):   
        item_to_insert = lyst[i]   # 待插入元素
        for j in range(i-1, -1, -1):  # 从第 i-1 往前循环,直到遇到比带插入元素小的
            if item_to_insert < lyst[j]:
                lyst[j + 1] = lyst[j]
            else:
                # 前面元素都比待插入元素小
                break
        lyst[j+1] = item_to_insert

4.快速排序

快速排序采取分而治之的策略,将列表分解成更小的子列表,随后,这些子列表再递归地排序。对于每个小的列表迭代比较计算次数肯定小的多,所以相较于n*n的循环嵌套,算法复杂度肯定更优越。具体方法如下:

1.首先,从列表的中间位置选取一项作为基准点

2.将列表中的项按基准点分区,使小于基准点的项位于基准点的左侧,大于基准点的项位于基准点右侧。

3.分而治之,对于子列表,重新选择基准点重复过程。

算法复杂度计算:

        分割次数:\lg2(n)

        x次分割计算:经过 x 次分割子列表数量为 2^{x}个,每个子列表元素个数为 \frac{n}{2^{x}}个,则需要计算比较 (\frac{n}{2^{_{x}}}-1)*2^{x}

       总计算比较次数: \sum_{x=1}^{\log 2(n)} (\frac{n}{2^{_{x}}}-1)*2^{x} =n*\lg 2(n)-(\frac{n+2}{2}*\lg 2(n))=\frac{n\log n}{2\log 2}-\frac{\log n}{\log 2}

       所以算法复杂度为O(n\log n)

快速排序的Python实现:

def quit_sort(lyst):
    quit_sort_helper(lyst, 0, len(lyst) - 1)


def quit_sort_helper(lyst, left, right):
    """
    排序方法
    递归
    也是数学归纳法的体现:如果 n=1, n=2成立,则一定能够n=k时成立。
    """
    if left < right:
        pivot_location = partition(lyst, left, right)
        quit_sort_helper(lyst, left, pivot_location-1)
        quit_sort_helper(lyst, pivot_location+1, right)


def partition(lyst, left, right):
    """
    将列表 left 至 right 之间的元素按基准点分开
    本算法不需要注重局部顺序,因为当分割至2个元素时肯定能够排序好
    """
    middle = left + (right - left) // 2  # 用减法计算中间,防止溢出变负
    pivot = lyst[middle]   # 取中间元素作为基准点
    lyst[middle], lyst[right] = lyst[right], pivot   # 首先将基准点放至最末端
    boundary = left  # 分界,最后也是基准点需要插入的坐标
    for index in range(left, right):
        if lyst[index] < pivot:
            lyst[index], lyst[boundary] = lyst[boundary], lyst[index]
            boundary + 1
    lyst[right], lyst[boundary] = lyst[boundary], lyst[right]

5.归并排序

归并排序的算法思想与快速排序类似,算法复杂度为O(n\log n)。其策略如下:

1.计算列表的中间位置,并递归地排序其左边和右边的子列表。

2.将两个排序好的子列表重新合并为单个排序好的列表

3.当子列表不能再划分的时候,停止这个过程。

归并排序Python实现:

def merge_sort(lyst):
    """
    归并排序函数
    传入 lyst , 函数运行结束,则 lyst 被排序好
    列表为可变对象,可以在函数中直接改变lyst
    """
    # copy_buffer 为额外消耗的内存空间
    # 为了节约分配空间的资源消耗,借助python可变对象的特性,首先初始化一个缓存列表
    # 其他函数中重复使用该缓存空间
    copy_buffer = [None for _ in lyst]
    merge_sort_helper(lyst, copy_buffer, 0, len(lyst) - 1)


def merge_sort_helper(lyst, copy_buffer, low, high):
    """
    递归思想
    """
    if low < high:
        middle = low + (high - low) // 2
        merge_sort_helper(lyst, copy_buffer, low, middle)
        merge_sort_helper(lyst, copy_buffer, middle + 1, high)
        merge(lyst, copy_buffer, low, middle, high)


def merge(lyst, copy_buffer, low, middle, high):
    """
    合并两个排序好的列表
    两个子列表: low----middle, middle+1----high
    """
    i1, i2 = low, middle + 1
    for i in range(low, high + 1):
        if i1 > middle:  # 第一个子列表已完成
            copy_buffer[i] = lyst[i2]
            i2 += 1
        elif i2 > high:  # 第二个子列表已完成
            copy_buffer[i] = lyst[i1]
            i1 += 1
        elif lyst[i1] < lyst[i2]:  # i1 较小
            copy_buffer[i] = lyst[i1]
            i1 += 1
        else:  # i2 较小
            copy_buffer[i] = lyst[i2]
            i2 += 1
    for i in range(low, high+1):
       # 将已经排序好的 缓存列表元素赋值给 lyst 对应位置
       lyst[i] = copy_buffer[i]

merge函数是将两个排序好的子列表合并到一个大的排序好的子列表中。第1子列表在low和middle之间,第2个子列表再middle+1和high之间。该函数包含三个步骤:

1.将索引指针设置为每个子列表的第一项。分别为low和middle+1

2.从每个子列表的第1项开始,重复的比较,将较小的项从其子列表中复制到复制缓存中,并且继续处理子列表中的下一项。重复这个过程直到两个列表中的项都复制过了。如果先到达了其中一个子列表的末尾,通过从另一个子列表复制剩余的项结束该步骤。

3.将copy_buffer中low和high之间的部分,复制回lyst中对应的位置处。

由此可见,合并排序空间复杂度为O(n)。

此博客提到的五种排序算法(冒泡排序、选择排序、插入排序、快速排序、归并排序)为基本排序算法。后面会继续写一篇博客来介绍希尔排序、堆排序、桶排序、基数排序、计数排序等五种算法思想及Python实现。

--------------------- 
作者:蒙蕤 
来源:CSDN 
原文:https://blog.csdn.net/Mengrui_1991/article/details/86479852 
版权声明:本文为博主原创文章,欢迎交流。如需转载,请附上博文链接。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值