常见的八种排序算法(动图理解 + Python代码实现)

由于需要即将实习面试的需要,所以现在在对一些算法抱一抱佛脚。首先学习的是几种常见的排序算法,网上也有人称:八大排序算法。本博客主要针对网上动图来对它们进行初步的认识。

主要参考大神博客:

  1. https://www.cnblogs.com/onepixel/articles/7674659.html
  2. https://blog.csdn.net/hguisu/article/details/7776068


图片来源:十大经典排序算法动画与解析,看我就够了!(配代码完全版)

测试用例:

q = [4, 67, 8, 12, 2, 82, 0, 7, 53, 96, 7, 23, 9, 4000, -1, 6, -3]

1. 交换——冒泡排序

冒泡排序
冒泡排序是属于交换排序当中的一种方法,其思想非常简单:从头开始两两比较(绿色的两个数字),大的数就往上冒,这样遍历一轮后,最大的数字就会直接筛选出来(动图中变为黄色)。然后再重复上述操作,即可完成第二大数的冒泡。以此类推,直到所有的数字排序完成。

代码
def BubbleSort(arr):
    for i in range(len(arr)):
        flag = 0 # 设置flag,若本身已经有序,则直接break
        for j in range(len(arr) - i - 1):
            if arr[j] > arr[j + 1]:
                flag = 1
                arr[j], arr[j+1] = arr[j+1], arr[j]
        if flag == 0: 
            break
    return arr

2. 交换——快速排序

快速排序
交换排序的另一种方式是快速排序,这个排序是目前使用非常广的方法,其也是使用交换的思想,同时还使用了分治的思想。

结合上面动图来进行解释,我们先设定一个“哨兵”(黄色的数),然后我们将比这个“哨兵”小的数字放在左边(绿),比他大的放右边(紫),然后结束操作后,这个数字变为橙色。

接着我们使用分治思想,对比“哨兵”小的,和比它大的两个总体分别使用前面的做法,进行递归,最终对所有数字进行排序。

代码
def QuickSort(arr):
    
    if len(arr) <= 1:
        return arr
    
    mid, left, right = arr[0], [], []
    for i in arr[1:]:
        if i < mid:
            left.append(i)
        else:
            right.append(i)
    
    return QuickSort(left) + [mid] + QuickSort(right)
    
QuickSort(q)

3. 选择——直接选择排序

选择排序
直接选择排序法,思想非常简单,我们就直接一个一个遍历,选出其中最小的,记录下来(红),遍历完一遍,交换到对应的次序后变为橙色,重复上述过程,最终按元素大小,从小到大排列。

代码
def SelectSort(arr):
    for i in range(len(arr) - 1):
        ind = i
        for j in range(i + 1, len(arr)):
            if arr[ind] > arr[j]:
                ind = j
        arr[i], arr[ind] = arr[ind], arr[i]
        
    return arr

4. 选择——堆排序

堆排序1
堆排序,稍微会比较复杂,其也是选择排序的一种。首先我们一步一步排成一棵近似二叉树结构,然后首先对每一个叶子结点的小三角进行排序,将最大的值往上冒,然后从底层一步一步往上遍历,最终处于根节点的就是所有值中,最大的值了。

接着,我们将根节点与最右边的叶子结点调换顺序,之后就不再进行考虑,然后对剩下的结点重复前面的操作,最终可以得到一个排好序的数列。

下面有另一个图来理解堆排序是怎样的一个过程:
堆排序2

代码
def Build(arr, root, end):
    while True:
        child = 2 * root + 1 # 由于堆是一个完全二叉树,所以可以根据其特性,推算出左子节点的位置
        if child > end: # 若左子节点超过了最后一个节点,则终止循环
            break
        if (child + 1 <= end) and (arr[child + 1] > arr[child]): # 若右子节点在最后一个节点之前,并且右子节点比左子节点大,则我们的孩子指针移到右子节点上
            child += 1
        if arr[child] > arr[root]: # 若最大的孩子节点大于根节点,则交换两者顺序,并且将根节点指针,移到这个孩子节点上
            arr[child], arr[root] = arr[root], arr[child]
            root = child
        else:
            break
    return arr

def HeapSort(arr):
    n = len(arr)
    first_root = n // 2 - 1 # 首先根据完全二叉树的性质,确认最深最后的那个根节点的位置
    for root in range(first_root, -1, -1): # 由后向前遍历所有的根节点,建堆并进行调整
        Build(arr, root, n - 1)
        
    for end in range(n - 1, 0, -1): # 调整完成后,将堆顶的根节点与堆内最后一个元素调换位置,此时为数组中最大的元素,然后重新调整堆,将最大的元素冒到堆顶。依次重复上述操作
        arr[0], arr[end] = arr[end], arr[0]
        Build(arr, 0, end - 1)
    return arr
    
HeapSort(q)

我们可以在原有堆排序基础上稍作修改,变为Topk的代码:

Topk代码
def Topk(arr, k):
    n = len(arr)
    if k >= n: # 首先判断,若k >= n,则输出整个数组
        return arr
    else:
        first_root = n // 2 - 1
        for root in range(first_root, -1, -1):
            Build(arr, root, n - 1)
            
        for end in range(n - 1, n - k - 1, -1): # 我们只需要循环k次,找出前k个最大元素即可
            arr[0], arr[end] = arr[end], arr[0]
            Build(arr, 0, end - 1)
        return arr[::-1][:k] # 最后找出尾部的k个元素

Topk(q, 10)

5. 插入——直接插入排序

插入排序
直接插入排序也是非常简单的一个排序方式,我们先将两个数字排好序(橙),然后往后选一个数字(红),在排好序的位置中找个合适的位置插入,然后变为橙色。接着重复上述操作,直到序列中的所有的数字排好序。


6. 插入——希尔排序

希尔排序
另一种更厉害一些的插入排序方法就是希尔排序,它会将数字分几遍挑出来进行排序。

预先我们指定一个增量序列:[5, 2, 1]

第一遍,我们每隔5个进行两两大小比较,大的排后面,小的排前面。

第二遍,我们每隔2个进行两两大小比较,大的排后面,小的排前面。

第三遍,我们每隔1个(相邻)进行两两大小比较,大的排后面,小的排前面。

最终就可得到我们的排序结果。


7. 归并排序

归并排序
归并排序也是一种非常高效的排序方式,其也用了分治的思想,具体的代码可以写成递归的形式。

我们首先将整个序列两两分开,然后每组中的两个元素排好序。接着就是组与组和合并,这个只需将两组所有的元素遍历一遍,即可按顺序合并。以此类推,最终所有组合并为一组时,整个数列就已经排好序了。

代码
def Merge(arr1, arr2): # 可参考有序链表的合并
    result = []
    while arr1 and arr2:
        if arr1[0] < arr2[0]:
            result.append(arr1.pop(0))
        else:
            result.append(arr2.pop(0))
    if arr1:
        result += arr1
    if arr2:
        result += arr2
    return result

def MergeSort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    return Merge(MergeSort(arr[:mid]), MergeSort(arr[mid:]))

MergeSort(q)

8. 基数排序

基数排序
基数排序是一种非常有意思的排序。对于数值偏小的一组序列,其速度是非常快的,时间复杂度达到了线性,而且思想也非常的巧妙。

首先我们按照个位数的大小,分别放入10个队列中,然后采用先进先出的原则,摆回去。继而再对十位数进行相同的操作。以此类推,直至最后最大一个数的那个数位结束了这种操作,最终我们就得到排好序的数列。

虽然这种排序方法是线性复杂度,但如果存在非常大的数,就会非常慢,以为线性复杂度的倍数会非常高,从而得不偿失,这点需要注意。


由于时间原因,最近只是针对不同排序算法的含义进行理解,后续会逐渐补充上Python实现的代码。

最后再补充另一个动图做的非常好的十大排序的博客:十大经典排序算法动画与解析,看我就够了!(配代码完全版)

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值