十大排序算法(上)--Python

前言

排序算法
排序算法是程序员平时编程中最常见也是最基本的算法之一

常见的排序算法包括:冒泡排序、选择排序、插入排序、归并排序、快速排序、计数排序、堆排序、希尔排序、桶排序、基数排序等等。他们的用途、时间复杂度和空间复杂度都有差别,本文将和大家一起学习探讨上述这些排序算法的代码以及相关特征。

一、冒泡排序

1、概述:

冒泡排序是一种常见且常用的排序算法。从无序区通过一次一次交换,将最大的元素放到有序区域内,就像泡泡,最大的泡泡先上浮。
(无序区域->有序区域)

2、原理:

  • 比较相邻的元素大小,如果说前一个比后一个数大,就交换,反之不交换
  • 对数组做顺序遍历操作,每对相邻的元素都做相同的操作,以此类推一直操作到最后一个元素,此时最大的数就像水里的泡泡一样,来到数组的最后一位
  • 重新回到第一位,再重复上述操作,到倒数第二位停下(因为最后一位已经是最大了),以此类推
  • 持续做上述操作,直到数组排序完成

3、代码

#冒泡排序
def bubble_sort(arr): # arr是需要排序的数组
    lenth = len(arr)
    # 外循环:未排序的区间为[0, i]
    for i in range(lenth):
    	# 内循环将未排序区域的[0, i]中的最大值交换至该区间的最右侧
        for j in range(lenth - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]

if __name__ == '__main__':
    tmp = [1,3,5,64,2,54,89,2,56,10]
    bubble_sort(tmp)
    print(tmp)

输出结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89]

通过程序我们可以发现,在排序过程中,如果已经排序完成,代码依旧会一直走下去,于是我们可以加一个标志位,当一次冒泡过程中没有出现任何的元素交换,则代表排序完成,可以减少程序运行速度。

# 优化版本
def bubble_sort_1(arr): # arr是需要排序的数组
    lenth = len(arr)
    # 外循环:未排序的区间为[0, i]
    for i in range(lenth):
        flag = False
        # 内循环将未排序区域的[0, i]中的最大值交换至该区间的最右侧
        for j in range(lenth - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                flag = True
        if not flag:    # 增加判断,如果本轮没有交换任何元素,说明排序完成,直接跳出
            break  

if __name__ == '__main__':
    tmp = [1,3,5,64,2,54,89,2,56,10]
    bubble_sort_1(tmp)
    print(tmp)

输出结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89] 

4、特征

时间复杂度:O(n²)
空间复杂度:O(1)
稳定排序:在冒泡过程中遇到相等的元素不会交换
内部排序

二、选择排序

1、概述:

是一种比较直观的排序方式,不过由于算法时间复杂度较高,适合数据量较少的时候。在无序区域找一个最小值(最大值)放在有序区域的后面(前面),相当于在无序区域遍历寻找符合条件的数字来扩充有序区域。

2、原理:

  • 先在数组中遍历找出找出最大或者最小的元素,将数字放在数组的起始位置(0还是lenth - 1取决于想要从到大还是从大到小)
  • 再从剩下的数组中寻找最大或者最小的元素,排到还未排序的末端
  • 重复上述步骤,一直到所有的元素排序完成

3、代码

#选择排序
def selection_sort(arr):
    lenth = len(arr)
    # 外循环是未排序的区间为[i, n - 1]
    for i in range(lenth - 1):
        tmp_min = i
        # 内循环是未找到排序区间内的最小元素
        for j  in range(i+1, lenth):
            if arr[j] < arr[tmp_min]:
                tmp_min = j
        # 将最小元素和未排序区间的首个元素交换
        arr[i], arr[tmp_min] = arr[tmp_min], arr[i]


if __name__ == '__main__':
    tmp = [1,3,5,64,2,54,89,2,56,10]
    selection_sort(tmp)
print(tmp)

输出结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89]

4、特征

时间复杂度:O(n²)
空间复杂度:O(1)
内部排序

三、插入排序

1、概述:

是一种直观的算法,将第一个元素看作有序的或者可以当作一个标杆,后续的元素都是无序的,将后续元素和第一个元素比较,小的话放到第一个元素左边(交换位置或者值),大的话放在第一个元素右边,这样就扩充了有序序列,再从头开始以此类推。

2、原理:

  • 选择第一个元素为有序序列
  • 向后取一个元素A,在有序序列中从后向前扫描,遇到小于该元素的元素B
  • 把该元素A插在已经排序的元素B的后面,形成新的有序序列
  • 重复上述动作,不断的将无序元素插入有序序列中,最终形成完整的排序

3、代码:

# 插入排序
def insertion_sort(arr):
    lenth = len(arr)
    # 外循环已经排序区间为[0, i-1]
    for i in range(1, lenth):
        tmp = arr[i]
        j = i - 1
        # 内循环将tmp值插入到已经排序的[0, i-1]区间的正确位置
        while j >= 0 and arr[j] > tmp:
            arr[j + 1] = arr[j]
            j -= 1
        # 将tmp值赋值到正确的位置
        arr[j + 1] = tmp


if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
    insertion_sort(tmp)
    print(tmp)

输出结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89, 100]

4、特征

时间复杂度:O(n²)
空间复杂度:O(1)
内部排序
在数据量较小时,速度较快

四、归并排序

1、概述

采用分治法的一种排序方法。就是把已经排好序的子序列进行合并,得到一个完整的有序序列,相对于先排序一片区域等排序完区域再拼接。

2、原理

  • 将长度为length的序列分成数目相等的两份子序列(lenth/2)
  • 对这两个子序列分别进行归并排序
  • 将两个自序列合并成为一个最终排好序的序列

3、代码

#归并排序
def merge_sort(arr): # arr是需要排序的数组
    lenth = len(arr)
    if (lenth <= 1):# 递归的边界条件
        return arr
    mid = lenth // 2# 求出数组个数的中位数,以此下标为界限分割数组
    llist, rlist = Merge_Sort(arr[:mid]), Merge_Sort(arr[mid:])# 调用函数分别为左右数组排序
    result = []
    Lenth_Llist = len(llist)
    Lenth_Rlist = len(rlist)
    i, j = 0, 0
    while i < Lenth_Llist and j < Lenth_Rlist:# while循环合并两个有序数组
        if rlist[j] < llist[i]:
            result.append(rlist[j])
            j += 1
        else:
            result.append(llist[i])
            i += 1
    result += llist[i:] + rlist[j:]# 把数组末添加的部分加到结果数组末尾
    return result

if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
print(merge_sort(tmp))

结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89, 100]

4、特征

时间复杂度:O(n logn)
空间复杂度:O(n)
外部排序

五、快速排序

1、概述

是处理大数据最快的一种排序方式,因为他的内部循环可以很有效率的实现,所以比一般的O(nlogn)算法更快

2、原理

  • 从数列中选出一个元素,作为一个基准
  • 重新排序,所有比基准小的放在数组左边,所有比基准大的数字放在数组右边,在这个分区结束后,这个基准数字就在数组的最中间,这个被称为分区操作
  • 利用递归把小于基准的子序列和大于基准的子序列进行排序,最终得到一个完整的排序数组

3、代码

#快速排序
def quick_sort(arr, start, end):
    if start > end:
        return
    Base_Value, left, right = arr[start], start, end # 设置基准数字为arr[start]
    while left < right:
        while arr[right] >= Base_Value and left < right:# 从右向左找到首个小于基准数字的元素
            right -= 1
        arr[left] = arr[right]# 交换元素
        while arr[left] < Base_Value and left < right:# 从左向右找到首个小于基准数字的元素
            left += 1
        arr[right] = arr[left]# 交换元素
    arr[left] = Base_Value # 更新基准数
    # 递归左子数组、右子数组
    Quick_Sort(arr, start, left - 1)
    Quick_Sort(arr, left + 1, end)


if __name__ == '__main__':
    tmp = [100, 1, 3, 5, 64, 2, 54, 89, 2, 56, 10]
    quick_sort(tmp, 0, len(tmp) - 1)
print(tmp)

结果:[1, 2, 2, 3, 5, 10, 54, 56, 64, 89, 100]

4、特征

时间复杂度:O(n logn)
空间复杂度:O(logn)
内部排序
比一般的O(nlogn)要快

六、总结

排序算法平均时间复杂度空间复杂度排序方式是否稳定
冒泡排序O(n²)O(1)内部排序稳定
选择排序O(n²)O(1)内部排序不稳定
插入排序O(n²)O(1)内部排序稳定
归并排序O(nlogn)O(n)外部排序稳定
快速排序O(nlogn)O(nlogn)内部排序不稳定
  • 32
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值