探索十大经典排序算法之美(基于Python)

这篇具有很好参考价值的文章主要介绍了探索十大经典排序算法之美(基于Python)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

在计算机科学的世界中,排序算法无疑是最为经典和基础的主题之一。排序不仅是解决各种计算问题的基础,而且在日常编程中也是必不可少的一环。Python这一富有表达力的编程语言,提供了许多强大的工具和库,使得实现和理解排序算法变得更加直观和有趣。

本篇博客将带领大家探索Python中一些常见的排序算法,从简单到复杂,深入剖析它们的工作原理、性能特点以及在实际应用中的巧妙之处。无论你是初学者还是有一定经验的开发者,通过本文的学习,你将更好地理解排序算法的本质,并能够在实际项目中明智地选择和应用合适的算法。

让我们开始这次关于Python排序算法之旅吧!

一、排序算法种类

在学习具体的排序算法之前,我们先简单了解一下,排序算法都有哪些吧!

我认为排序算法可以大致分为以下几类:

探索十大经典排序算法之美(基于Python),排序算法,算法,python,数据结构

 大致了解了排序算法有哪些后,我们来聊聊具体算法的实现过程以及排序的思想

二、简单排序算法

1.冒泡排序

  • 算法实现过程

通过比较相邻的元素,如果前一个比后一个大,就把它们两个对调位置。

一趟排序完成后,则无序区减少一个数,有序区增加一个数。

对序列中的每一对相邻元素做同样的工作,直到序列全部完成。

冒泡循环需要遍历n-1次(n为序列的长度) 。

  • 时间复杂度 o(n2)
  • 代码实现
def bubble_sort(li):
    for i in range(len(li)-1):
        exchange = False
        for j in range(len(li)-i-1):
            if li[j] > li[j+1]:
                li[j], li[j+1] = li[j+1], li[j]
                exchange = True
        if not exchange:
            return

最外层循环指的是冒泡循环需要遍历n-1次(n为列表长度),内层循环到n-i-1的原因是比如第一趟过后,顶端最大的数字已经找出,并且这个最大的数已经在有序区中了,只需要用同样的方法遍历剩下的无序区,找出无序区中最大的数即可。

这里也介绍了交换两个数的方法,即  li[i], li[i+1] = li[i+1], li[i] 。

exchange是一个标志位,他的作用是如果在某一趟排序中,没有发生任何交换,此时可以说明无序区本来就是已经排列好的,不需要进行后续的排序。这里每一趟之后都需要检验,所以注意缩进。

这里默认是升序排列,若要改为降序排列,只需要将判断语句中的‘>’改为‘<’即可。

2.选择排序

  • 算法实现过程

每一轮从待排序的记录中选出最小的元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小元素,然后放到已排序的序列的末尾。

也可以每一轮找出数值最大的元素,这样的话,排序完毕后的数组最终是从大到小排列。

选择排序每次选出最小(最大)的元素,因此需要遍历 n-1 次。

  • 时间复杂度 o(n2)

注意:该选择排序的时间复杂度是o(n2),而不是o(n),这是因为在循环体中内置函数min()和remove()的复杂度都是o(n),而不是o(1),这两个函数的本质都是将整个序列遍历一遍,从而找出最小值和要删除的值

  • 代码实现
def select_sort_simple(li):
    li_new = []
    for i in range(len(li)):
        min_val = min(li)
        li_new.append(min_val)
        li.remove(min_val)
    return li_new

建立一个新的列表,遍历原列表,用内置函数min()找出列表中的最小值,把他存储在变量min_val中,在新建列表中加入这个值,并且将这个值从原始列表中删除,重复以上操作。 

  • 代码优化

由于以上代码需要再申请一块新的内存来存放排好序的列表,浪费空间,所以我们想办法对代码进行优化,我们可以把选择出来的最小的数与列表第一个位置的数进行交换,后面我只需要找无序区数中的最小值与无序区的第一个数进行比较交换,这样就能实现原地排序,节省了大量内存。由于要交换位置,那么就要记录下标,由于上面说到要与无序区的第一个数进行交换,所以min_loc的下标要记作i,后面只需要遍历无序区即可,所以j的范围是从i+1到n。

def select_sort(li):
    for i in range(len(li)):
        min_loc = i  # 遍历的是无序区的位置
        for j in range(i + 1, len(li)):  # 前包后不包,所以这里后面应当写列表长度,从i+1开始可以省去自己和自己比较的那一步
            if li[j] < li[min_loc]:
                min_loc = j
        li[i], li[min_loc] = li[min_loc], li[i]
        print(li)

3.插入排序 

  • 算法实现过程

插入排序就是每一步都将一个需要排序的数据按其大小插入到已经排序的数据序列中的适当位置,直到全部插入完毕。插入排序就如同打扑克牌一样,每次将后面抽到的牌按顺序插到前面已经排好序的牌中。

探索十大经典排序算法之美(基于Python),排序算法,算法,python,数据结构

  • 时间复杂度 o(n2) 
  • 代码实现
def insert_sort(li):
    for i in range(1, len(li)):  # 前包后不包
        tmp = li[i]
        j = i - 1  # j指的是手里的牌的下标
        while j >= 0 and li[j] > tmp:
            li[j + 1] = li[j]
            j = j - 1
        li[j + 1] = tmp

最外层的循环i的范围是从1到n(n为序列的长度),这是因为我的目的是遍历所有牌和我手中的作比较,相当于每次抽取一张牌,和我手中已有的牌数进行比较,然后把它们插入在合适的位置中,我抽取的第一张牌下标为0,所以我只需要从1开始比较就可以,所以i的范围从1开始。 

j表示我手里被比较的那张牌,如果j这张牌,比我新抽到的牌数值大,那么就需要j这张牌往右挪一个;如果j这张牌比我新抽到的牌数值小,我就应当把新抽到的牌插在j这张牌的右边。

tmp变量中存储的是我新抽到的牌。

三、高效排序算法

1.快速排序

  • 算法实现过程

 先从数据序列中取出一个数作为基准数(习惯取第一个数)。

分区过程,将比基准数小的数全放到它的左边,大于或等于它的数全放到它的右边。

再对左右区间递归重复第二步,直到各区间只有一个数。

  • 时间复杂度 o(nlogn)
  • 代码实现
def partition(li, left, right):
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:  # 从右边开始找比tmp小的数
            right -= 1  # right往左走一步
        li[left] = li[right]  # 把右边的值写在左边的空位上
        while left < right and li[left] < tmp:
            left += 1
        li[right] = li[left]  # 把左边的值写在右边的空位上
    li[left] = tmp  # 把tmp归位
    return left
def quick_sort(data, left, right):
    if left < right:  # 说明至少两个元素
        mid = partition(data, left, right)  # 说明第一个元素已经归位,mid把列表分成两部分
        quick_sort(data, left, mid - 1)  # 递归调用这两部分
        quick_sort(data, mid + 1, right)

2.堆排序

  • 算法实现过程

1. 建立大根堆

2. 得到堆顶元素,为最大元素

3. 去掉堆顶,将堆顶最后一个元素放到堆顶,此时可以通过一次调整使堆有序

4. 堆顶元素为第二大元素

5. 重复步骤三,直到堆变空

  • 时间复杂度 nlg(n)
  • 代码实现
def sift(li, low, high):
    """

    :param li: 列表
    :param low: 堆的根部节点位置(堆顶)
    :param high: 堆的最后一个节点的位置

    """
    i = low                    # i最开始指向根节点
    j = 2 * i + 1              # j开始指向根节点的左孩子
    tmp = li[low]              # 把堆顶元素存起来
    while j <= high:           # 只要j位置有数则一直循环
        if j + 1 <= high and li[j+1] > li[j]:    # 如果右孩子比左孩子大,并且要保证右孩子要有(不越界)
            j = j+1            # 把就指向右孩子
        if li[j] > tmp:
            li[i] = li[j]
            i = j              # 往下看一层
            j = 2 * i + 1
        else:                  # 说明tmp更大,把tmp放在i的位置上
            li[i] = tmp        # 把tmp防灾某一级领导的位置上
            break
    else:
        li[i] = tmp            # 把tmp放到叶子节点上

def heap_sort(li):
    n = len(li)
    for i in range((n-2)//2, -1, -1):# i表示建堆的时候调整的部分的根的下标
        sift(li, i, n-1)             # 建堆完成
    for i in range(n-1, -1, -1):     # i指向当前堆的最后一个元素
        li[0], li[i] = li[i], li[0]  # 让堆顶元素和最后一个元素做交换
        sift(li, 0, i-1)             # i-1是新的high
  • 堆排序的应用——topk问题

现有n个数,设计算法得到前K大的数

解题思路:取列表前K个元素建立一个小根堆。堆顶就是目前第K大的数。依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素;如果大于堆顶,则将堆顶更换为该元素,并且对堆进行一次调整,遍历列表所有元素后,倒叙弹出堆顶

def sift(li, low, high):
    i = low
    j = 2 * i + 1
    tmp = li[low]
    while j <= high:
        if j+1 <= high and li[j+1] < li[j]:
            j = j+1
        if li[j] < tmp:
            li[i] = li[j]
            i = j
            j = 2 * i + 1
        else:
            break
        li[i] = tmp

def topk(li, k):
    heap = li[0:k]        # 把0到K的数取出来
    for i in range((k-2)//2, -1, -1):
        sift(heap, i, k-1)
    for i in range(k, len(li)-1):
        if li[i] > heap[0]:
            heap[0] = li[i]
            sift(heap, 0, k-1)

    for i in range(k - 1, -1, -1):
        heap[0], heap[i] = heap[i], heap[0]
        sift(heap, 0, i - 1)
    return heap

 3.归并排序

  • 算法实现过程

1.分解:将列表越分越小,直到分成一个元素 2.终止条件:一个元素是有序的 3.合并:将两个有序列表合并,列表越来越大。

  •  时间复杂度 o(nlgn)
  • 代码实现
def merge(li, low, mid, high):
    i = low
    j = mid + 1
    ltmp = []
    while i <=mid and j <= high: # 只要左右两边都有数
        if li[i] < li[j]:        # 比较i和j指向的两个数的大小关系
            ltmp.append(li[i])
            i += 1
        else:                    # j指向的比i指向的大
            ltmp.append(li[j])
            j += 1
    # while循环结束的时候,两部分肯定有一部分没数了
    while i <= mid:              # 当j没有剩余时
        ltmp.append(li[i])
        i += 1
    while j <= high:             # 当i没有剩余时
        ltmp.append(li[j])
        j += 1
    li[low:high+1] = ltmp

def merge_sort(li, low, high):
      if low < high: # 至少有两个元素,递归
          mid = (low + high)//2
          merge_sort(li, low, mid)
          merge_sort(li, mid+1, high)
          merge(li, low, mid, high)

四、其他排序算法

1.希尔排序

  • 算法实现过程

希尔排序是一种分组插入插入排序算法,首先取一个整数d1=n/2,将元素分为d1个组,每组相邻量元素之间的距离为d1,在各组内进行直接插入排序;取第二个整数d2=d/2,重复上述分组排序过程,直到d1=1,即所有元素在同一组内进行直接插入排序;希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟使得所有数据有序。

  • 时间复杂度

希尔排序1的时间复杂度讨论比较复杂,并且和选取的gap序列有关

  • 代码实现
def insert_sort_gap(li, gap):
    for i in range(gap, len(li)):
        tmp = li[i]
        j = i - gap
        while j >= 0 and li[j] > tmp:
            li[j+gap] = li[j]
            j -= gap
        li[j+gap] = tmp

def shell_sort(li):
    d = len(li) // 2
    while d >= 1:
        insert_sort_gap(li, d)
        d //= 2

2.计数排序

  • 算法实现过程

假设列表中的数字都在0到100之间,计算数字出现的次数,然后按顺序列出这些数字。

  • 时间复杂度 o(n)
  • 代码实现
def count_sort(li, max_count=100):
    count = [0 for _ in range(max_count+1)]
    for val in li:
      count[val] += 1
    li.clear()
    for ind, val in enumerate(count):
        for i in range(val):
            li.append(ind)

3.计数排序的引申——桶排序

  • 算法实现过程

在计数排序中,如果元素的范围比较大(比如在1到1亿之间),我们应当如何改进算法?

桶排序:首先将元素分在不同的桶中,在每个桶中的元素排序,eg:我有以下这些数,我讲他们按大小放在下面五个桶中

探索十大经典排序算法之美(基于Python),排序算法,算法,python,数据结构

  • 时间复杂度 

桶排序的时间复杂度取决于数据的分布,也就是需要对不同数据排序时采取不同的分同策略

  • 代码实现
def bucket_sort(li, n=100, max_num=10000):
    buckets = [[] for _ in range(n)]        # 创建n个桶
    for var in li:
        i = min(var // (max_num // n), n-1)           # i 表示var放到几号桶里
        buckets[i].append(var)              # var加在桶里面
        for j in range(len(buckets[i])-1, 0, -1):
            if buckets[i][j] < buckets[i][j-1]:
                buckets[i][j], buckets[i][j-1] = buckets[i][j-1], buckets[i][j]
            else:
                break
    sort_li = []
    for buc in buckets:
        sort_li.extend(buc)
    return sort_li

4.基数排序

  • 算法实现过程

排序过程中,将元素分层为多个关键码进行排序(一般按照数值的个位、十位、百位、…… 进行区分),多关键码排序按照从最主位关键码到最次位关键码或从最次位到最主位关键码的顺序逐次排序。文章来源地址https://www.toymoban.com/news/detail-833846.html

  • 时间复杂度 o(kn)
  • 代码实现
def radix_sort(li):
    max_num = max(li)
    it = 0
    while 10 ** it <= max_num:
        buckets = [[] for _ in range(10)]
        for var in li:
            digit = (var // 10 ** it) % 10
            buckets[digit].append(var)
        li.clear()
        for buc in buckets:
            li.extend(buc)

        it += 1

到了这里,关于探索十大经典排序算法之美(基于Python)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】十大经典排序算法-插入排序

    🌟 个人博客: www.hellocode.top 🏰 Java知识导航: Java-Navigate 🔥 CSDN: HelloCode. 🌞 知乎 :HelloCode 🌴 掘金 :HelloCode ⚡如有问题,欢迎指正,一起学习~~ 插入排序(Insertion Sort)是一种简单直观的排序算法,其基本思想是将一个记录插入到已排好序的有序序列中,直到所有记录

    2024年02月13日
    浏览(83)
  • 十大经典排序算法

    1、 冒泡排序(Bubble Sort):相邻元素比较,逐步将最大元素“冒泡”到序列最后。时间复杂度O(n^2)。 2、选择排序(Selection Sort):从序列中选择最小的元素,放到序列的起始位置,再从剩余元素中选择最小的元素放到已排序序列的末尾。时间复杂度O(n^2)。 3、插入排序(In

    2024年02月09日
    浏览(40)
  • 十大经典排序算法(上)

    目录 1.1冒泡排序 1. 算法步骤  3.什么时候最快 4. 什么时候最慢 5.代码实现 1.2选择排序 1. 算法步骤  2. 动图演示 3.代码实现  1.3 插入排序 1. 算法步骤 2. 动图演示 3. 算法实现 1.4 希尔排序 1. 算法步骤 2. 动图演示  3.代码实现 1.5 归并排序 1. 算法步骤  2. 动图演示  3.代码实现

    2024年02月02日
    浏览(37)
  • C语言之十大经典排序算法

            嗨喽,大家好,我是程序猿老王,程序猿老王就是我。         今天给大家讲一讲C语言十大经典排序算法原理与实现。 目录 一、排序算法背景 二、十大经典排序算法的由来 三、十大经典排序算法的复杂度 四、十大经典排序算法讲解 1.冒泡排序(Bubble Sort)

    2023年04月09日
    浏览(39)
  • 头歌数据结构实训参考---十大经典排序算法

    可通过 目录 快速查阅对应排序算法

    2024年02月04日
    浏览(64)
  • 【Python排序算法】一文掌握十大排序算法,冒泡排序、插入排序、选择排序、归并排序、计数排序、基数排序、希尔排序和堆排序

    目录 1 冒泡排序(Bubble Sort) 2 插入排序(Insertion Sort) 3 选择排序(Selection Sort) 4. 快速排序(Quick Sort) 5. 归并排序(Merge Sort) 6 堆排序 (Heap Sort) 7 计数排序 (Counting Sort) 8 基数排序 (Radix Sort) 9 希尔排序(Shell Sort) 10 桶排序     1 冒泡排序(Bubble Sort)        冒泡排序

    2024年02月08日
    浏览(55)
  • 探索经典算法 拓扑排序,字符串匹配算法,最小生成树

    拓扑排序、字符串匹配算法和最小生成树是计算机科学中常用的数据结构和算法,它们在解决各种实际问题中具有重要的应用价值。在本文中,我将详细介绍这三个主题,并提供相应的示例代码和应用场景,以帮助读者更好地理解和应用这些概念。 一、拓扑排序: 拓扑排序

    2024年02月05日
    浏览(52)
  • 探索数据之美:初步学习 Python 柱状图绘制

    pyecharts 是一个基于 Echarts 的 Python 图表库,它提供了丰富的图表类型和交互功能。可以使用使用 pyecharts 创建柱状图 首先,安装 pyecharts 库。如果没有安装,可以使用以下命令安装: 然后,创建一个简单的柱状图: 在这个示例中,首先导入 pyecharts 的必要模块。然后,创建一

    2024年02月12日
    浏览(45)
  • Python之花舞盛宴:探索列表与元组之美与妙用

    在Python编程世界中,列表和元组是两个最常用的数据结构之一。无论是初学者还是经验丰富的开发者,对于这两个数据类型的掌握都至关重要。 列表和元组都是用于存储多个值的容器,但它们在性质和特性上有所不同。列表是可变的,可以随意修改、添加或删除其中的元素,

    2024年02月11日
    浏览(44)
  • 【经典算法】 leetcode88.合并排序的数组(Java/C/Python3实现含注释说明,Easy)

    作者主页: 🔗进朱者赤的博客 精选专栏:🔗经典算法 作者简介:阿里非典型程序员一枚 ,记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法( 公众号同名 ) ❤️觉得文章还不错的话欢迎大家点赞👍➕收藏⭐️➕评论,💬支持博主,记得点个大大的 关

    2024年04月22日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包