七大排序算法python实现版

本文介绍了Python实现的七大排序算法:冒泡排序、选择排序、插入排序、归并排序、快速排序、桶排序和堆排序。详细阐述了每种排序算法的基本思想、过程和平均时间复杂度,提供代码实现,并提及希尔排序的原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先讲解的网上很多,我直接上python代码

其中原理讲解可以参考:原理讲解一原理讲解2

注意下表是平均时间,笔试面试可能会问最好最坏

排序算法平均时间复杂度
冒泡排序O(n2)
选择排序O(n2)
插入排序O(n2)
希尔排序O(n1.5)
快速排序O(N*logN)
归并排序O(N*logN)
堆排序O(N*logN)
基数排序O(d(n+r))

1、冒泡排序


  • 基本思想:两个数比较大小,较大的数下沉,较小的数冒起来。

  • 过程:

    • 比较相邻的两个数据,如果第二个数小,就交换位置。
    • 从后向前两两比较,一直到比较最前两个数据。最终最小数被交换到起始的位置,这样第一个最小数的位置就排好了。
    • 继续重复上述过程,依次将第2.3...n-1个最小数排好位置。

       

  • 平均时间复杂度:O(n2)

def bubblesort(seq):
    for i in range(len(seq)):
        for j in range(len(seq) - 1, i, -1):
            if seq[j] < seq[j - 1]:
                seq[j - 1], seq[j] = seq[j], seq[j - 1]
    return seq

s = [4, 6, 2, 5, 7, 9, 8, 1]
bubblesort(s)
print(s)

  2、选择排序

  • 基本思想:
    在长度为N的无序数组中,第一次遍历n-1个数,找到最小的数值与第一个元素交换;
    第二次遍历n-2个数,找到最小的数值与第二个元素交换;
    。。。
    第n-1次遍历,找到最小的数值与第n-1个元素交换,排序完成。

  • 过程:

     

  • 平均时间复杂度:O(n2)

def selectsort(seq):
    for fillslot in range(len(seq)-1,0,-1):
        pos = 0
        for loc in range(1,fillslot):
            if seq[loc] > seq[pos]:
                pos = loc
        seq[pos], seq[fillslot] = seq[fillslot], seq[pos]
    return seq

3、插入排序

  • 基本思想:
    在要排序的一组数中,假定前n-1个数已经排好序,现在将第n个数插到前面的有序数列中,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。

  • 过程:

    插入排序

    相同的场景

  • 平均时间复杂度:O(n2)

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
        插入排序
        由于其内层循环非常紧凑,对于小规模的输入,
        插入排序是一种非常快的原址排序算法
        注: 如果输入数组中仅有常数个元素需要在排序过程中存储在数组外,
            则称这种排序算法是原址的。
"""

def insertsort(seq):
    for index in range(1, len(seq)):
        currentvalue = seq[index]
        pos = index
        while pos > 0 and seq[pos-1] > currentvalue:
            seq[pos] = seq[pos-1]
            pos -= 1
        seq[pos] = currentvalue
    return seq

4、归并排序

def mergesort(seq):
    """归并排序"""
    if len(seq) <= 1:
        return seq
    mid = len(seq) // 2  # 将列表分成更小的两个列表
    # 分别对左右两个列表进行处理,分别返回两个排序好的列表
    left = mergesort(seq[:mid])
    right = mergesort(seq[mid:])
    # 对排序好的两个列表合并,产生一个新的排序好的列表
    return merge(left, right)

def merge(left, right):
    """合并两个已排序好的列表,产生一个新的已排序好的列表"""
    result = []  # 新的已排序好的列表
    i = 0  # 下标
    j = 0
    # 对两个列表中的元素 两两对比。
    # 将最小的元素,放到result中,并对当前列表下标加1
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result += left[i:]
    result += right[j:]
    return result

  5、快排

  • 基本思想:(分治)

    • 先从数列中取出一个数作为key值;
    • 将比这个数小的数全部放在它的左边,大于或等于它的数全部放在它的右边;
    • 对左右两个小数列重复第二步,直至各区间只有1个数。
  • 辅助理解:挖坑填数

    • 初始时 i = 0; j = 9; key=72
      由于已经将a[0]中的数保存到key中,可以理解成在数组a[0]上挖了个坑,可以将其它数据填充到这来。
      从j开始向前找一个比key小的数。当j=8,符合条件,a[0] = a[8] ; i++ ; 将a[8]挖出再填到上一个坑a[0]中。
      这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。
      这次从i开始向后找一个大于key的数,当i=3,符合条件,a[8] = a[3] ; j-- ; 将a[3]挖出再填到上一个坑中。
      数组:72 - 6 - 57 - 88 - 60 - 42 - 83 - 73 - 48 - 85
       0   1   2    3    4    5    6    7    8    9
    • 此时 i = 3; j = 7; key=72
      再重复上面的步骤,先从后向前找,再从前向后找。
      从j开始向前找,当j=5,符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;
      从i开始向后找,当i=5时,由于i==j退出。
      此时,i = j = 5,而a[5]刚好又是上次挖的坑,因此将key填入a[5]。
      数组:48 - 6 - 57 - 88 - 60 - 42 - 83 - 73 - 88 - 85
       0   1   2    3    4    5    6    7    8    9
    • 可以看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间重复上述步骤就可以了。
      <数组:48 - 6 - 57 - 42 - 60 - 72 - 83 - 73 - 88 - 85
       0   1   2    3    4    5    6    7    8    9
  • 平均时间复杂度:O(N*logN)

'''O(nlog n)
   找枢纽值,然后分而治之
   '''
def quicksort(seq):
    if len(seq)<=1:
        return seq
    else:
        pivot = seq[0]
        small = [i for i in seq[1:] if i < pivot]
        big = [j for j in seq[1:] if j >=pivot]
        return quicksort(small) + [pivot] + quicksort(big)

6、桶排序

#! /usr/bin/env python
#coding=utf-8
'''
 桶排序
 复杂度O(n)
 '''
def countsort( A, d ):  #A待排数组, d位数 如果位置需要先修改一下知道A中最多位数
    for k in range(d):  #k轮排序
        s[ [] for i in range(10) ]  #数字0~9,建10个桶
        '''对于数组中的元素,首先按照最低有效数字进行
           排序,然后由低位向高位进行'''
        for i in A:
            s[ i//(10**k)%10 ].append(i)
        A = [j for i in s for j in i]  #977/10=97(小数舍去),87/100=0
    return A

7、堆排序

  • 基本思想:

    6660

    • 图示: (88,85,83,73,72,60,57,48,42,6)

      7770

      Heap Sort

  • 平均时间复杂度:O(NlogN)
    由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。二次操作时间相加还是O(N * logN)。

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
"""
        堆排序
        堆排序的时间复杂度是O(nlg(n)),并且具有空间原址性
        二叉堆heap是一种数据结构,可用来实现优先队列
        给定一个节点的下标i(下标从0开始),则其父节点、坐孩子、右孩子坐标:
        parent(i) = floor((i+1)/2 - 1) = ((i + 1) >> 1) - 1
        left(i) = 2*i + 1 = (i << 1) + 1
        right(i) = 2*i + 2 = (i + 1) << 1
        最小堆定义: 所有i必须满足A[parent(i)] <= A[i]
        最大堆定义: 所有i必须满足A[parent(i)] >= A[i]
        在堆排序中,我们使用最大堆
        在优先队列算法中,使用最小堆
"""

class Heap():
    def __init__(self, seq, heapSize, length):
        """
        seq: 存放待排序的序列
        heapSize: 堆的大小
        lenght: 整个序列大小
        """
        self.seq = seq
        self.heapSize = heapSize
        self.length = length


def heapSort(seq):
    """
    堆排序算法
    """
    heap = Heap(seq, len(seq), len(seq))
    __buildMaxHeap(heap)
    s = heap.seq
    for i in range(heap.length - 1, 0, -1):
        s[0], s[i] = s[i], s[0]
        heap.heapSize -= 1
        __maxHeapify(heap, 0)


def __maxHeapify(heap, i):
    """
    前提是i的两棵子树left(i)和right(i)的二叉树都是最大堆了
    现在加入i节点后,要保持这个二叉树为最大堆的性质
    heap: Heap数据结构
    """
    seq = heap.seq
    slen = heap.heapSize
    while True:
        left = (i << 1) + 1
        right = (i + 1) << 1
        if left < slen and seq[left] > seq[i]:
            largest = left
        else:
            largest = i
        if right < slen and seq[right] > seq[largest]:
            largest = right
        if largest != i:
            seq[largest], seq[i] = seq[i], seq[largest]
            i = largest
        else:
            break


def __buildMaxHeap(heap):
    """
    由完全二叉树的性质可知:对于 n/2..n-1为下标的元素,都是叶子节点,
    那么可从下标floor((i+1)/2 - 1)开始往前到0的元素调用maxHeapify
    heap: Heap数据结构
    """
    slen = heap.heapSize
    for i in range(((slen + 1) >> 1) - 1, -1, -1):
        __maxHeapify(heap, i)

8、希尔排序(代码暂未研究)  

  • 前言:
    数据序列1: 13-17-20-42-28 利用插入排序,13-17-20-28-42. Number of swap:1;
    数据序列2: 13-17-20-42-14 利用插入排序,13-14-17-20-42. Number of swap:3;
    如果数据序列基本有序,使用插入排序会更加高效。

  • 基本思想:
    在要排序的一组数中,根据某一增量分为若干子序列,并对子序列分别进行插入排序。
    然后逐渐将增量减小,并重复上述过程。直至增量为1,此时数据序列基本有序,最后进行插入排序。

  • 过程:

    希尔排序

参考资料:

1.  https://www.runoob.com/w3cnote/sort-algorithm-summary.html

2. 原理讲解1原理讲解2

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值