小白教程

 找回密码
 立即注册
小白教程 首页 Python技术文章 查看内容

Python实现四大经典排序算法

发布者: 小白教程

一、前言

在面试的问题中可能会遇到排序算法,毕竟作为一个程序员,掌握排序算法是非常重要的。本文总结了用python实现的八种经典排序算法。排序算法是数据结构和算法中最基本的算法之一。排序算法可以分为内部排序和外部排序。内部排序是对内存中的数据记录进行排序,而外部排序是访问外部内存,因为排序的数据太大,不能一次保存所有的排序记录。常见的内部排序算法有:插入排序、希尔排序、选择排序、冒泡排序、合并排序、快速排序、堆排序、基数排序等。

二、冒泡排序

冒泡排序是一种简单的排序算法。它反复访问要排序的序列,一次比较两个元素,如果它们的顺序不对,就交换它们。重复访问序列的工作,直到不需要交换为止,也就是说,序列已经被排序。这个算法的名字来自这样一个事实:元素越小,它就越慢地浮到序列的顶部。这个算法的名字来自这样一个事实:越小的元素会慢慢“浮动”的顺序(升序或降序)通过交换,就像二氧化碳泡沫的碳酸饮料最终将浮到顶部,所以它被称为“泡沫排序”。
算法原理:
比较相邻的元素,如果第一个大于第二个,交换两个。
对每一对相邻的元素做同样的处理,从开始的第一对到结束的最后一对。此时,最后一个元素应该是最大的数。
对除最后一个元素外的所有元素重复上述步骤。
对于越来越少的元素,继续重复上述步骤,直到没有可比较的数字对为止。
演示过程如下:

def bubble_sort(nums):
    for pass_num in range(len(nums) - 1, 0, -1):   # n-1趟
        flag = True    # 每一趟置flag为True
        for i in range(pass_num):
            if nums[i] > nums[i + 1]:
                nums[i], nums[i + 1] = nums[i + 1], nums[i]
                flag = False     # 有交换:flag置为False
        # 每一趟结束后大的数会往后冒泡
        # flag为True  说明到这趟已经没有交换 提前跳出循环 提高算法效率
        # print(nums)
        if flag:
            break
    return nums


s = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
print(s)
print(bubble_sort(s))

# 结果如下:
[3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

无序表中初始数据项的排列对冒泡排序没有影响,算法处理总需要n-1次。随着次数的增加,比较次数逐渐从n-1减少到1,包括可以排序的次数
可能发生的数据项的交换。比较的时间复杂度是O (n)²)。
在交换次数上,时间复杂度也是o (N2)。一般来说,每次交换包括三个任务。最好的情况是,列表在排序之前是有序的,并且交换的数量是0;最糟糕的情况是,每次比较都必须进行交换。交换的次数等于比较的次数。平均情况是最坏情况的一半。
冒泡排序通常被用作一种时间效率较低的排序算法,作为其他算法的基准。其效率主要较低。每个数据项在找到最终位置之前,都要经过多次的比较和交换,大部分操作都是无效的。
冒泡排序的优点是它不需要任何额外的存储空间。

三、选择排序

选择性排序是一种简单直观的排序算法,无论输入什么数据,它的时间复杂度是O (n)²)。所以当你使用它时,数据越小越好。唯一的优势可能是它不会占用额外的内存空间。
算法原理:
首先,找到无序序列中的最小(大)元素,并将其存储在排序序列的开头
然后我们继续从剩余的无序元素中找到最小(最大)的元素,然后把它放在排序后的序列的末尾。
重复第二步,直到所有元素都排序完毕。
动态图如下:
算法实现:

def select_sort(nums):
    for pass_num in range(len(nums) - 1, 0, -1):   # n-1趟
        pos_max = 0
        for location in range(1, pass_num + 1):
            if nums[location] > nums[pos_max]:
                pos_max = location
        # 每一趟只交换一次
        nums[pass_num], nums[pos_max] = nums[pos_max], nums[pass_num]

    return nums

s = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
print(s)
print(select_sort(s))


# 结果如下:
[3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
在冒泡排序的基础上改进了选择排序,保留了多遍比较的基本思想,每遍都使当前最大项到位。
然而,选择排序减少了交换。与冒泡排序相比,交换进行了多次。每次交换只进行一次。记录最大项目的位置,然后对这次的最后一个项目进行交换。
排序的时间复杂度略好于冒泡排序,且比较次数相同,或者说O (n)好于冒泡排序²,但是交换次数减少到O (n)。

四、插入排序

虽然插入排序的代码实现不像冒泡排序和选择排序那样简单和粗糙,但它的原理应该是最容易理解的,因为只要人们玩过扑克牌,他们就应该能够理解。插入排序是最简单直观的排序算法。其工作原理是将无序的数据按排序顺序从后往前扫描,找到对应的位置并插入。与冒泡排序一样,插入排序也有一种称为半分割插入的优化算法。
算法原理:
将第一个待排序序列中的第一个元素视为有序序列,最后一个元素后面的第二个元素视为无序序列。
对无序序列进行从头到尾的扫描,将扫描到的每个元素插入到有序序列的适当位置(如果要插入的元素与有序序列中的一个元素相等,则将要插入的元素插入到相等的元素之后)。
算法实现:

def insert_sort(arr):
    for index_ in range(1, len(arr)):
        position = index_
        current_value = arr[index_]    # 插入项
        # 比对 移动  直到找到第一个比它小的项
        while position > 0 and arr[position - 1] > current_value:
            arr[position] = arr[position - 1]
            position = position - 1
        # 插入新项
        arr[position] = current_value

    return arr


s = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
print(s)
print(insert_sort(s))
# 结果如下:
[3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48]
[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
插入排序的比较主要用于查找“新项”的插入位置。最坏的情况是,每次传递都要与子列表中的所有项进行比较。总的比较次数与冒泡排序相同,次数相同
阶数仍然是o (n)²)
在最好的情况下,当列表是有序的,每次只需要进行一次比较,总数是O (n)。
因为move操作只包含一个赋值,是swap操作的1 / 3,所以插入排序的性能会更好。

Archiver|手机版|小黑屋|小白教程 ( 粤ICP备20019910号 )

GMT+8, 2024-9-19 12:13 , Processed in 0.019049 second(s), 15 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc. Template By 【未来科技】【 www.wekei.cn 】

返回顶部