数据结构经典排序算法——python(详细汇总)

python算法学习笔记

  • 前言
  • 一、冒泡排序
    • 1.1 算法步骤
    • 1.2 图解
    • 1.3 排序效率
    • 1.4 Python代码实现
  • 二、选择排序
    • 2.1 算法步骤
    • 2.2 图解
    • 2.3 Python代码实现
  • 三、插入排序
    • 3.1 算法步骤
    • 3.2 图解
    • 3.3 排序效率
    • 3.4 Python代码实现
  • 四、希尔排序
    • 4.1 算法步骤
    • 4.2 图解
    • 4.3 排序效率
    • 4.4 Python代码实现
  • 五、归并排序
    • 5.1 算法步骤
    • 5.2 图解
    • 5.3 Python代码实现
  • 六、快速排序
    • 6.1 算法步骤
    • 6.2 图解
    • 6.3 Python代码实现
  • 七、堆排序
    • 7.1 算法步骤
    • 7.2 图解
    • 7.3 Python代码实现
  • 八、计数排序
    • 8.1 算法步骤
    • 8.2 图解
    • 8.3 Python代码实现
  • 九、桶排序
    • 9.1 算法步骤
    • 9.2 图解
    • 9.3 排序效率
    • 9.4 Python代码实现
  • 十、基数排序
    • 10.1 图解
    • 10.2 Python代码实现
  • 总结

前言

内容大多引用菜鸟教程,写完了才发现教程讲的非常详细,我这里只针对python的代码实现

排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存
数据结构经典排序算法——python(详细汇总)_第1张图片
数据结构经典排序算法——python(详细汇总)_第2张图片

名词解释:

  • n:数据规模
  • k:"桶"的个数
  • In-place:占用常数内存,不占用额外内存
  • Out-place:占用额外内存
  • 稳定性:排序后 2 个相等键值的顺序和排序之前它们的顺序相同

一、冒泡排序

1.1 算法步骤

  • 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
  • 针对所有的元素重复以上的步骤,除了最后一个。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

1.2 图解

数据结构经典排序算法——python(详细汇总)_第3张图片

1.3 排序效率

(1)当输入的数据已经是正序时,就不需要冒泡排序了
(2)当输入的数据是反序时,写一个 for 循环反序输出数据就行,没必要麻烦的用冒泡排序

1.4 Python代码实现

def bubble_sort(nums):
    for i in range(1, len(nums) - 1):  # 设置冒泡排序进行的次数
        for j in range(0, len(nums) - i):  # j为列表下标
            if nums[j] > nums[j + 1]:
                nums[j], nums[j + 1] = nums[j + 1], nums[j] #交换两值
    return nums
 
print(bubble_sort([45, 32, 8, 33, 12, 22, 19, 97]))
# 输出:[8, 12, 19, 22, 32, 33, 45, 97]

二、选择排序

数据规模越小越好

2.1 算法步骤

  • 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
  • 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  • 重复第二步,直到所有元素均排序完毕。

2.2 图解

数据结构经典排序算法——python(详细汇总)_第4张图片

2.3 Python代码实现

def selectionSort(nuns):
    for i in range(len(nums) - 1):
        # 记录最小数的索引
        minIndex = i
        for j in range(i + 1, len(nums)):
            if nums[j] < nums[minIndex]:
                minIndex = j
        # i 不是最小数时,将 i 和最小数进行交换
        if i != minIndex:
            nums[i], nums[minIndex] = nums[minIndex], nums[i]
    return nums

三、插入排序

  • 工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入
  • 是简单排序中效率最好的一种

3.1 算法步骤

  • 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
  • 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

3.2 图解

数据结构经典排序算法——python(详细汇总)_第5张图片

3.3 排序效率

(1)当输入的数据已经是正序时,需要进行的比较操作是 (n-1)次
(2)当输入的数据是反序时,需要进行的比较共有n(n-1)/2次

3.4 Python代码实现

def insertionSort(nums):
    for i in range(len(nums)):
        preIndex = i-1
        current = nums[i] # 保存当前元素值,拿前面的值和它比较
        while preIndex >= 0 and nums[preIndex] > current:
            nums[preIndex+1] = nums[preIndex]  # 将前面较大的元素向后移
            preIndex -= 1
        nums[preIndex+1] = current  #最后将其保存到相应位置
    return nums

四、希尔排序

  • 递减增量排序算法

4.1 算法步骤

  • 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
  • 按增量序列个数 k,对序列进行 k 趟排序;
  • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度

4.2 图解

数据结构经典排序算法——python(详细汇总)_第6张图片

4.3 排序效率

(1)当输入的数据已经是正序时,需要进行的比较操作是 (n-1)次
(2)当输入的数据是反序时,需要进行的比较共有n(n-1)/2次

4.4 Python代码实现

def shellSort(nums):
    import math
    gap=1
    while(gap < len(nums)/3):  # 动态定义间隔序列
        gap = gap*3+1  # 这里目的最后必然有一次间隔为1的插入排序,防止有些元素没有比较排序
    while gap > 0:
        for i in range(gap,len(nums)):
            temp = nums[i]
            j = i-gap
            while j >=0 and nums[j] > temp:  #对每组使用插入排序
                nums[j+gap]=nums[j]
                j-=gap
            nums[j+gap] = temp
        gap = math.floor(gap/3)  #向下取整,最后一趟排序必然为间隔为1的插入排序,由于上面定义的间隔序列规则
    return nums

五、归并排序

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,归并排序的实现由两种方法:

  • 自上而下的递归(所有递归的方法都可以用迭代重写,所以就有了第 2 种方法);在 JavaScript 中这种方式不太可行,因为这个算法的递归深度对它来讲太深了。
  • 自下而上的迭代;

5.1 算法步骤

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  4. 重复步骤 3 直到某一指针达到序列尾;
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

5.2 图解

数据结构经典排序算法——python(详细汇总)_第7张图片

5.3 Python代码实现

def mergeSort(nums):
    import math
    if(len(nums) < 2):
        return nums
    middle = math.floor(len(nums) / 2)
    left, right = nums[0:middle], nums[middle:]
    print(left,right)
    return merge(mergeSort(left), mergeSort(right))#这里递归较复杂,看不懂建议带数一步一步来

def merge(left,right):
    result = []
    while left and right:
        if left[0] <= right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0))
    while left:
        result.append(left.pop(0))
    while right:
        result.append(right.pop(0))
    return result

if __name__ == '__main__':
    a = [14, 2, 34, 43, 21, 19]
    print (mergeSort(a))

六、快速排序

  • 分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists)
  • 算是在冒泡排序基础上的递归分治法

6.1 算法步骤

  1. 从数列中挑出一个元素,称为 “基准”(pivot);
  2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;

6.2 图解

数据结构经典排序算法——python(详细汇总)_第8张图片

6.3 Python代码实现

def quickSort(nums, left=None, right=None):
    # isinstance()  函数来判断一个对象是否是一个已知的类型,类似type()
    left = 0 if not isinstance(left,(int, float)) else left   # if else语法糖
    right = len(nums)-1 if not isinstance(right,(int, float)) else right
    if left < right:
        partitionIndex = partition(nums, left, right)
        quickSort(nums, left, partitionIndex-1)
        quickSort(nums, partitionIndex+1, right)
    return nums

# 分治
def partition(nums, left, right):
    pivot = left  #基准元素为左边第一个元素
    index = pivot+1  #index保存数组中比基准元素小的最后一个元素的位置,用于最后基准元素的替换
    i = index
    while  i <= right:
        if nums[i] < nums[pivot]: # 值小的放基准元素左边
            nums[i], nums[index] = nums[index], nums[i]  #交换元素
            index += 1
        i += 1
    nums[pivot], nums[index-1] = nums[index-1], nums[pivot]  #交换元素,将基准元素放置大小分离的中间
    return index-1

# 示例:
array = [4,3,2,6,5,6,2,7,5,8,4]
print(quickSort(array))
# 第一趟快排结果 [3,2,2,  4  ,5,6,6,7,5,8,4]
# 输出为[1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 9, 9, 10, 12, 15, 15, 17]

alt+j 多次点击可以一次性选中并修改多个相同的变量


七、堆排序

  • 堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点
  • 分为小顶堆、大顶堆

7.1 算法步骤

  1. 创建一个堆 H[0……n-1];
  2. 把堆首(最大值)和堆尾互换;
  3. 把堆的尺寸缩小 1,并调用 shift_down(0),目的是把新的数组顶端数据调整到相应位置;
  4. 重复步骤 2,直到堆的尺寸为 1。

7.2 图解

7.3 Python代码实现

def buildMaxHeap(nums):
    import math
    for i in range(math.floor(len(nums)/2),-1,-1):
        heapify(nums,i)

def heapify(nums, i):
    left = 2*i+1
    right = 2*i+2
    largest = i
    if left < numsLen and nums[left] > nums[largest]:
        largest = left
    if right < numsLen and nums[right] > nums[largest]:
        largest = right

    if largest != i:
        swap(nums, i, largest)
        heapify(nums, largest)

def swap(nums, i, j):
    nums[i], nums[j] = nums[j], nums[i]

def heapSort(nums):
    global numsLen
    numsLen = len(nums)
    buildMaxHeap(nums)
    for i in range(len(nums)-1,0,-1):
        swap(nums,0,i)
        numsLen -=1
        heapify(nums, 0)
    return nums

八、计数排序

  • 核心在于将输入的数据值转化为键存储在额外开辟的数组空间中
  • 要求输入的数据必须是有确定范围的整数
  • 当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k) —— 线性时间

8.1 算法步骤

  1. 找出待排序的数组中最大和最小的元素
  2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
  3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
  4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

8.2 图解

数据结构经典排序算法——python(详细汇总)_第9张图片

8.3 Python代码实现

def countingSort(nums, maxValue):
    bucketLen = maxValue+1
    bucket = [0]*bucketLen
    sortedIndex =0
    numsLen = len(nums)
    for i in range(numsLen):
        if not bucket[nums[i]]:
            bucket[nums[i]]=0
        bucket[nums[i]]+=1
    for j in range(bucketLen):
        while bucket[j]>0:
            nums[sortedIndex] = j
            sortedIndex+=1
            bucket[j]-=1
    return nums

九、桶排序

  • 计数排序的升级版。它利用了函数的映射关系
  • 为了使桶排序更加高效,需要做到这两点:
  1. 在额外空间充足的情况下,尽量增大桶的数量
  2. 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中

9.1 算法步骤

  • 选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
  • 按增量序列个数 k,对序列进行 k 趟排序;
  • 每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度

9.2 图解

数据结构经典排序算法——python(详细汇总)_第10张图片

9.3 排序效率

(1)最快:当输入的数据可以均匀的分配到每一个桶中
(2)最慢:当输入的数据被分配到了同一个桶中

9.4 Python代码实现

def bucketSort(nums, maxNum):
    buf = {
     i: [] for i in range(int(maxNum)+1)}  # 不能使用[[]]*(max+1),这样新建的空间中各个[]是共享内存的
    nums_len = len(nums)
    for i in range(nums_len):
        num = nums[i]
        buf[int(num)].append(num)  # 将相应范围内的数据加入到[]中
    nums = []
    for i in range(len(buf)):
        if buf[i]:
            nums.extend(sorted(buf[i]))  # 这里还需要对一个范围内的数据进行排序,然后再进行输出
    return nums

if __name__ == "__main__":
    lis = [3.1, 4.2, 3.3, 3.5, 2.2, 2.7, 2.9, 2.1, 1.55, 4.456, 6.12, 5.2, 5.33, 6.0, 2.12]
    print(bucketSort(lis, max(lis)))

十、基数排序

  • 一种非比较型整数排序算法
  • 原理是将整数按位数切割成不同的数字,然后按每个位数分别比较
  • 基数排序 vs 计数排序 vs 桶排序,这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:
  1. 基数排序:根据键值的每位数字来分配桶;
  2. 计数排序:每个桶只存储单一键值;
  3. 桶排序:每个桶存储一定范围的数值;

10.1 图解

数据结构经典排序算法——python(详细汇总)_第11张图片

10.2 Python代码实现

def radixSort(nums):
    """基数排序"""
    i = 0 # 记录当前正在排拿一位,最低位为1
    maxNum = max(nums)  # 最大值
    j = len(str(maxNum))  # 记录最大值的位数
    while i < j:
        bucketList =[[] for _ in range(10)] #初始化桶数组
        for x in nums:
            bucketList[int(x / (10**i)) % 10].append(x) # 找到位置放入桶数组
        print(bucketList)
        nums.clear()
        for x in bucketList:   # 放回原序列
            for y in x:
                nums.append(y)
        i += 1

if __name__ == '__main__':
    nums = [334,5,67,345,7,345345,99,4,23,78,45,1,3453,23424]
    radixSort(nums)
    print(nums)

总结

数据结构经典排序算法——python(详细汇总)_第12张图片

你可能感兴趣的:(学习笔记,算法工程师,python,算法,数据结构,排序算法)