10种排序算法总结(Python 版)

2023-11-09

1. 冒泡排序( O ( n 2 ) O(n^2) O(n2))

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  3. 针对所有的元素重复以上的步骤,除了最后一个;
  4. 重复步骤1~3,直到排序完成。
def bubbleSort(nums):
    if not nums:
        return None
    
    for i in range(0, len(nums)):
        for j in range(0, len(nums)-i-1):
            if nums[j+1] < nums[j]:
                temp = nums[j]
                nums[j] = nums[j+1]
                nums[j+1] = temp
    
    return nums

# test
nums = [4,1,5,2,3]
bubbleSort(nums)
[1, 2, 3, 4, 5]

2. 快速排序( O ( n l o g n ) O(nlogn) O(nlogn))

原理:利用分治的思想,将原数组分成两个部分,其中左边比中间数字小,右边比中间数字大。而这这划分的数字就是每个子数组的最后一个。
使用partition函数对数组进行重排,并返回当前划分的位置,再利用递归调用,对每个子数组进行重排,直到所有元素都完成重排。

def partition(A, p, r):
    x = A[r]
    i = p - 1
    for j in range(p, r):
        if A[j] <= x:
            i += 1
            temp = A[j]
            A[j] = A[i]
            A[i] = temp
    temp = A[r]
    A[r] = A[i+1]
    A[i+1] = temp
    return i+1 

def quicksort(A, p, r):
    if p < r:
        q = partition(A, p, r)
        quicksort(A, p, q-1)
        quicksort(A, q+1, r)

A = [2, 8, 7, 1, 3, 5, 6, 4]
quicksort(A, 0, len(A)-1)
A
[1, 2, 3, 4, 5, 6, 7, 8]

3. 简单插入排序( O ( n 2 ) O(n^2) O(n2))

一般来说,插入排序都采用in-place在数组上实现。具体算法描述如下:

  1. 从第一个元素开始,该元素可以认为已经被排序;
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描;
  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置;
  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
  5. 将新元素插入到该位置后;
  6. 重复步骤2~5。
def insertionSort(nums):
    if not nums:
        return None
    
    for i in range(len(nums)-1):
        current = nums[i+1]
        preIndex = i
        while preIndex >=0 and current < nums[preIndex]:
            nums[preIndex + 1] = nums[preIndex]
            preIndex -= 1
        nums[preIndex + 1] = current
    
    return nums

# test
nums = [4,1,5,2,3]
insertionSort(nums)
[1, 2, 3, 4, 5]

4. 希尔排序( O ( n log ⁡ n ) O(n\log n) O(nlogn))

  1. 我们来看下希尔排序的基本步骤,在此我们选择增量gap=length/2,缩小增量继续以gap = gap/2的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2…1},称为增量序列。希尔排序的增量序列的选择与证明是个数学难题,我们选择的这个增量序列是比较常用的,也是希尔建议的增量,称为希尔增量,但其实这个增量序列不是最优的。此处我们做示例使用希尔增量。
  2. 先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:
  3. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  4. 按增量序列个数k,对序列进行k 趟排序;
  5. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
def shellSort(nums):
    if not nums:
        return None
    
    length = len(nums)
    gap = length // 2
    
    while gap > 0:
        for i in range(gap, length):
            temp = nums[i]
            preIndex = i - gap
            while preIndex >= 0 and nums[preIndex] > temp:
                nums[preIndex + gap] = nums[preIndex]
                preIndex -= gap
            nums[preIndex + gap] = temp
        gap = gap // 2
    
    return nums

#test
nums = [4,1,5,2,3]
shellSort(nums)
[1, 2, 3, 4, 5]

5. 简单选择排序( O ( n 2 ) O(n^2) O(n2))

n个记录的直接选择排序可经过n-1趟直接选择排序得到有序结果。具体算法描述如下:

  1. 初始状态:无序区为R[1…n],有序区为空;
  2. 第i趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为R[1…i-1]和R(i…n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第1个记录R交换,使R[1…i]和R[i+1…n]分别变为记录个数增加1个的新有序区和记录个数减少1个的新无序区;n-1趟结束,数组有序化了。
def selectionSort(nums):
    if not nums:
        return None
    
    for i in range(len(nums)):
        minIndex = i
        for j in range(i, len(nums)):
            if nums[j] < nums[minIndex]:
                minIndex = j
        temp = nums[i]
        nums[i] = nums[minIndex]
        nums[minIndex] = temp
        
    return nums

# test
nums = [4,1,5,2,3]
selectionSort(nums)
[1, 2, 3, 4, 5]

6. 堆排序[ O ( n log ⁡ n ) O(n\log n) O(nlogn)]

原理:通过构造最大堆,每次把堆顶元素放到数组尾部,再调整最大堆的结构,直到最大堆的元素个数小于2。

伪代码:

维护最大堆: MAX-HEAPIFY
MAX-HEAPIFY(A, i):
​ l = LEFT(i) #(2i)
​ r = RIGHT(i) #(2i + 1)
​ if l <= A.heap_size and A[l] > A[i]:
​ largest = l
​ else:
​ largest = i
​ if r <= A.heap_size and A[r] > A[largest]:
​ largest = r
​ if largest != i:
​ exchange A[i] with A[largest]
​ MAX-HEAPIFY(A, largest)

生成最大堆:BUILD-MAX-HEAPIFY(A)
BUILD-MAX-HEAPIFY(A):
​ A.heap_size = A.length
​ for i = [A.length/2] downto 2:
​ MAX-HEAPIFY(A, i)

堆排序:HEAPSORT(A)
HEAPSORT(A):
​ BUILD-MAX-HEAPIFY(A)
​ for i = A.length downto 2:
​ exchange A[1] with A[i]
​ A.heap_size = A.heap_size - 1
​ MAX-HEAPIFY(A, 1)

def max_heapify(A, i, len):
    '''
    维护最大堆
    '''
    l = 2*i + 1
    r = 2*i + 2
    
    if l < len and A[l] > A[i]:
        largest = l
    else:
        largest = i
    if r < len  and A[r] > A[largest]:
        largest = r
    
    if largest != i:
        temp = A[i]
        A[i] = A[largest]
        A[largest] = temp
        max_heapify(A, largest, len)
    return A

def build_max_heapify(A):
    '''
    构建最大堆
    '''
    for i in range(len(A) // 2-1, -1, -1):
        max_heapify(A, i, len(A))
        
def heapsort(A):
    '''
    堆排序
    '''
    build_max_heapify(A)
    for i in range(len(A)-1, 0, -1):
        temp = A[i]
        A[i] = A[0]
        A[0] = temp
        max_heapify(A, 0, i)
    


a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
heapsort(a)
a
[1, 2, 3, 4, 7, 8, 9, 10, 14, 16]

7. 归并排序( O ( n log ⁡ n ) O(n\log n) O(nlogn))

  1. 把长度为n的输入序列分成两个长度为n/2的子序列;
  2. 对这两个子序列分别采用归并排序;
  3. 将两个排序好的子序列合并成一个最终的排序序列。
def mergeSort(nums):
    if len(nums) < 2:
        return nums
    mid = len(nums) // 2
    left = nums[ :mid]
    right = nums[mid: ]
    
    return merge(mergeSort(left), mergeSort(right))

def merge(left, right):
    res = []
    l, r = 0, 0
    for i in range(len(left)+len(right)):
        if l >= len(left):
            res.append(right[r])
            r += 1
        elif r >= len(right):
            res.append(left[l])
            l += 1
        elif left[l] > right[r]:
            res.append(right[r])
            r += 1
        else:
            res.append(left[l])
            l += 1
    
    return res

# test
nums = [4,1,5,2,3]
mergeSort(nums)
[1, 2, 3, 4, 5]

8. 计数排序( O ( n + k ) O(n+k) O(n+k))

  1. 找出待排序的数组中最大和最小的元素;
  2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
  4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
    【只能对整数进行排序】
def countingSort(nums):
    if not nums:
        return None
    
    minNum, maxNum = nums[0], nums[0]
    for i in range(len(nums)):
        if nums[i] > maxNum:
            maxNum = nums[i]
        if nums[i] < minNum:
            minNum = nums[i]
    
    bias = 0 - minNum
    counter = [0]*(maxNum - minNum + 1)
    for i in nums:
        counter[bias + i] += 1
    
    index = 0
    i = 0
    while index < len(nums):
        if counter[i] != 0:
            nums[index] = i - bias
            counter[i] -= 1
            index += 1
        else:
            i += 1
    return nums

# test
nums = [4,1,5,2,3]
countingSort(nums) 
[1, 2, 3, 4, 5]

9. 桶排序( O ( n + k ) O(n+k) O(n+k))

  1. 设置一个定量的数组当作空桶子。
  2. 寻访序列,并且把数据一个一个放到对应的桶子去。
  3. 对每个不是空的桶子进行排序。
  4. 从不是空的桶子里把项目再放回原来的序列中。
def bucketSort(nums, bucketSize):
    if not nums or bucketSize < 1:
        return None
    
    buckets = [[] for i in range(bucketSize)]
    
    # find max and min
    minNum, maxNum = nums[0], nums[0]
    for i in nums:
        if i > maxNum:
            maxNum = i
        if i < minNum:
            minNum = i
    
    gap = (maxNum+1 - minNum) / bucketSize
    
    for num in nums:
        insert(buckets[int((num - minNum) / gap)], num)
        
    # concat all bucket
    res = []
    for i in buckets:
        res += i
    
    return res
    
def insert(array, num):
    if array == [] or array[-1] <= num:
        array.append(num)
    else:
        for i in range(len(array)):
            if array[i] > num:
                array.insert(i, num)
                break


# test
nums = [63, 157, 189, 51, 101, 47, 141, 121, 157, 156, 194, 117, 98, 139, 67, 133, 181, 13, 28, 109]
res = bucketSort(nums, 5)
print(sorted(nums))
print(res)
[13, 28, 47, 51, 63, 67, 98, 101, 109, 117, 121, 133, 139, 141, 156, 157, 157, 181, 189, 194]
[13, 28, 47, 51, 63, 67, 98, 101, 109, 117, 121, 133, 139, 141, 156, 157, 157, 181, 189, 194]

10. 基数排序( O ( n ∗ k ) O(n*k) O(nk))

  1. 取得数组中的最大数,并取得位数;
  2. arr为原始数组,从最低位开始取每个位组成radix数组;
  3. 对radix进行计数排序(利用计数排序适用于小范围数的特点)。
def radixSort(nums):
    if not nums:
        return 
    
    # 1. calculate the maximum number
    maxNum = nums[0]
    for i in nums:
        if maxNum < i:
            maxNum = i
            
    # 2. calculate the maximum digit
    maxDigit = 0
    while maxNum != 0:
        maxNum = maxNum // 10
        maxDigit += 1
    
    # 3. radix sort
    bucket = [[] for i in range(10)]
    div = 1
    for i in range(maxDigit):
        # save to bucket
        for j in range(len(nums)):
            bucket[(nums[j] // div) % 10].append(nums[j])
        
        # release to nums
        index = 0
        for k in range(10):
            while len(bucket[k]) != 0:
                nums[index] = bucket[k].pop(0)
                index += 1
        
        div *= 10
    
    return nums

# test
nums = [63, 157, 189, 51, 101, 47, 141, 121, 157, 156, 194, 117, 98, 139, 67, 133, 181, 13, 28, 109]
res = radixSort(nums)
print(res)
[13, 28, 47, 51, 63, 67, 98, 101, 109, 117, 121, 133, 139, 141, 156, 157, 157, 181, 189, 194]
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

10种排序算法总结(Python 版) 的相关文章

随机推荐

  • C# 获取System.Color 中所有颜色

    将 System Color 中的全部颜色提取出来 经过简单筛选后打乱顺序 做成随机颜色数组 用于存取取出的颜色对象 List
  • node + alipay-sdk 沙箱环境简单测试电脑网站支付

    正式上线需要上传营业执照 不知道怎么去申请一个 使用沙箱测试 首先前往支付宝开放平台控制台可看到左下方的沙箱测试链接 然后设置接口加签方式 选择系统默认密钥 系统默认密钥 gt 公钥模式 gt 查看 相关密钥分3种 应用公钥 应用私钥 选择
  • nestjs:使用nodemailer发送邮件提示mailer Error: Unexpected socket close

    问题 如上 参考 javascript nodemailer Connection closed unexpectedly Stack Overflow 解决办法 如果端口用的465 需要加上 secure true 之前没加导致上述报错
  • qt中将按钮指向的鼠标变成手型

    具体操作的方式有两种 一种是直接通过界面来进行更改 如下 第二种 就是使用代码的形式 ui gt pushButton gt setCursor QCursor Qt PointingHandCursor
  • unix时间戳c语言源码

    在单片机程序开发中 经常会遇到做数据存储 利用时间信息做数据的搜索查询 时间格式最好还是用unix时间戳的形式 可以直接对时间进行加减运算 从RTC中读取的时间一般都是BCD码 如何转换成unix时间戳 简单的做了实现 并开N台电脑从0开始
  • meidaPlayer java.io.IOException: setDataSource failed.: status=0x800000

    1 权限
  • pfSense多拨网速叠加教程

    0 废话 前后一共折腾了两天 发现网上很少有pfsense的多拨教程 查了好多资料终于摸索出来了方法 记录一下 坐标山东 联通光纤入户 200兆 实测稳定在250兆 赞 但是上行只有40兆 最后弄完 下行到了450M 一超过500就全部掉线
  • 杭电OJ 1003 Max Sum

    Max Sum 页面数据来自 this page from http acm hdu edu cn showproblem php pid 1003 Time Limit 2000 1000 MS Java Others Memory Li
  • Leetcode-1991. Find the Middle Index in Array

    Topic Background Given a 0 indexed integer array nums find the leftmost iddleIndex i e the smallest amongst all the poss
  • vben:vue3后台管理项目框架

    前言 Vue Vben Admin 是一个基于 Vue3 0 Vite Ant Design Vue TypeScript 的后台解决方案 目标是为开发中大型项目提供开箱即用的解决方案 包括二次封装组件 utils hooks 动态菜单 权
  • c++ const & constexpr c++98 c++11 c++14

    文章目录 c const 和 constexpr 知识点总结 一 const 1 const修饰变量 修饰普通变量 常量 修饰指针类型 修饰引用类型 2 const修饰函数 const修饰函数参数 const修饰函数返回值 const修饰成
  • 接口超时分析

    原文 接口突然超时 1 网络异常 1 1 网络抖动 经常上网的我们 肯定遇到过这样的场景 大多数情况下我们访问某个网站很快 但偶尔会出现网页一直转圈 加载不出来的情况 有可能是你的网络出现了抖动 丢包了 网页请求API接口 或者接口返回数据
  • Ubuntu16.04下caffe安装编译全过程(CPU)

    caffe是深度学习最好用的框架之一 但caffe的安装编译过程相对较复杂 本人在安装编译时百度了好几个版本 都没有一次成功过 因此在此总结一下自己的编译过程 本文是在Ubuntu16 04下安装编译caffe 其他版本会略有不同 该教程本
  • com.alibaba.druid.support.logging.JakartaCommonsLoggingImpl.

    问题 IDEA调试JDBC出错 com alibaba druid support logging JakartaCommonsLoggingImpl error create connection SQLException url jdb
  • 外包测试3年,离职后成功入职阿里,拿到offer的那天我泪目了...

    一提及外包测试 大部分人的第一印象就是 工作强度大 技术含量低 没有归属感 外包工作三年总体感受就是这份工作缺乏归属感 心里总有一种落差 进步空间不大 接触不到核心技术 公司没有针对你的技术培训与探究 工作简单 业务重复 通常是工具人的存在
  • QDockWidget布局方式

    上图为DockWidget多控件效果图 QDockWidget dock QLatin1String Last filters QWidget multiWidget new QWidget QVBoxLayout layout new Q
  • oracle生成不同uuid,oracle生成uuid

    select sys guid from dual gt 78AE331ADB2B4CE7AB598B1317B39D58 但该函数如下问题 1 返回类型为RAW 2 没有 dash 分隔符 3 返回的字母大写 为了使产生的uuid符合rf
  • vue3之createApp分析

    函数定义 createApp函数定义在文件 packages runtime dom src index ts中 export const createApp args gt const app ensureRenderer createA
  • Python 贝叶斯在文本分类的应用案例

    关注微信公共号 小程在线 关注CSDN博客 程志伟的博客 1 1 文本编码技术简介 1 1 1 单词计数向量 在开始分类之前 我们必须先将文本编码成数字 一种常用的方法是单词计数向量 在这种技术中 一个样本可以包 含一段话或一篇文章 这个样
  • 10种排序算法总结(Python 版)

    文章目录 1 冒泡排序 O n 2 2 快速排序 O nlogn 3 简单插入排序 O n 2 4 希尔排序 O n log n 5 简单选择排序 O n 2 6 堆排序 O n log n 7 归并排序 O n log n 8 计数排序