算法学习-排序算法汇总、python实现、对比

算法总述

本博客在学习北京大学陈斌老师《数据结构与算法》MOOC课程中总结反思形成。

排序算法是基础算法,但是还是有很多算法没有细致了解过,本文主要整理和实现冒泡排序、选择排序、插入排序、谢尔排序、归并排序、快速排序六种排序算法,并对六种算法进行对比如下

算法总结

  • 冒泡、选择和插入排序是 O ( n 2 ) O(n^2) O(n2)的算法;
  • 谢尔排序在插入排序的基础上进行了改进,采用对递增子表排序的方法,其时间复杂度可以在 O ( n ) O(n) O(n) O ( n 2 ) O(n^2) O(n2)之间,约为 O ( n 3 2 ) O(n^{\frac{3}2}) On23)
  • 归并排序的时间复杂度是 O ( n l o g n ) O(nlog n) O(nlogn),但归并的过程需要额外存储空间;(将归并排序分为两个过程来分析:分裂
    归并。分裂的过程时间复杂度为 O ( l o g n ) O(log n) O(logn),归并的过程,相对于分裂的每个部分,其所有数据项都会被比较和放置一次,所以
    是线性复杂度,其时间复杂度是 O ( n ) O(n) O(n))
  • 快速排序最好的时间复杂度是 O ( n l o g n ) O(nlog n) O(nlogn),也不需要额外的存储空间,但如果分裂点偏离列表中心的话,最坏情况下会退化
    O ( n 2 ) O(n^2) O(n2)(快速排序过程分为两部分:分裂移动。如果分裂总能把数据表分为相等的两部分,那么就是 O ( l o g n ) O(log n) O(logn)的复杂度;
    而移动需要将每项都与中值进行比对,还是 O ( n ) O(n) O(n))。

冒泡排序

算法思路简述

冒泡排序对无序表进行多趟比较交换,每趟包括了多次两两相邻比较,并将逆序的数据项互换位置,最终能将本趟的最大项就位。

自己实现版

def bubbleSort01(numList):
    for i in range(len(numList)):
        for j in range(i, len(numList)):
            if numList[i] > numList[j]:
                numList[i], numList[j] = numList[j], numList[i]

老师官方版

def bubbleSort(alist):
    # n-1趟
    for passnum in range(len(alist) - 1, 0, -1):
        for i in range(passnum):
            # 序错,交换
            if alist[i] > alist[i + 1]:
                alist[i], alist[i + 1] = alist[i + 1], alist[i]

老师改进版

def shortBubbleSort(alist):
    exchange = True
    passnum = len(alist) - 1
    while passnum > 0:
        exchange = False
        for i in range(passnum):
            if alist[i] > alist[i + 1]:
                exchange = True
                alist[i], alist[i + 1] = alist[i + 1], alist[i]
        passnum = passnum - 1

对比小结

自己实现的版本,是选定一个元素缓慢冒泡,指导思想的是单个元素的下沉动作。

老师实现的版本更符合冒泡的定义,相邻两两比较,直接将最大的浮到最高层,指导思想是浮的动作。

当然,实际上两个版本的效果一样,只是编写代码实现的上稍有差异。

选择排序

算法思路简述

选择排序对冒泡排序的交换进行化简,每趟仅进行1次交换,记录最大项的所在位置,最后再跟本趟最后一项交换。

自己实现版

def selectionSort01(numList):
    for i in range(len(numList)):
        _t = i
        for j in range(i, len(numList)):
            if numList[j] < numList[_t]:
                _t = j
        numList[i], numList[_t] = numList[_t], numList[i]

老师官方版

def selectionSort(alist):
    for fillslot in range(len(alist) - 1, 0, -1):
        positionOfMax = 0
        for location in range(1, fillslot + 1):
            if alist[location] > alist[positionOfMax]:
                positionOfMax = location
        alist[fillslot], alist[positionOfMax] = alist[positionOfMax], alist[fillslot]

对比小结

选择排序和冒泡排序本质没有不同,区别在于选择排序减少了交换的操作,使用了记录位置的方法,可以稍微缓解复杂度。

实现上看,我的版本思维逻辑是下沉逻辑,老师的版本是上浮逻辑。

插入排序

算法思路简述

插入排序维持一个已排好序的子列表,其位置始终在列表的前部,然后逐步扩大这个子列表直到全表。

自己实现版

def InsertSort(numList):
    numList = numList.copy()
    for i in range(len(numList)):
        _sortedList = numList[0:i]
        _insertNum = numList[i]
        for indexJ,j in enumerate(_sortedList):
            if _insertNum < j:
                for k in range(indexJ,i):
                    numList[k+1] = numList[k]
                numList[indexJ] = _insertNum
                break
    return numList

老师官方版

def insertionSort(alist):
    alist = alist.copy()
    for index in range(1,len(alist)):
        # 新项,插入项
        currentvalue = alist[index]
        position = index
        # 比对,移动
        while position > 0 and alist[position-1] > currentvalue:
            alist[position] = alist[position-1]
            position = position-1
        # 插入新项
        alist[position] = currentvalue
    return alist

对比小结

自己实现的版本代码简洁性和可读性有点差,但是"插入"的核心逻辑得到体现。

自己实现的版本偏好于使用"for"循环,老师实现的版本"while"更简洁。

自己实现的版本的位移是定位后整体移动,老师的版本是和比对、移动一体化。

谢尔排序

算法思路简述

谢尔排序以插入排序作为基础,对无序表进行“间隔”划分子列表,每个子列表都执行插入排序。

老师官方版

def shellSort(alist):
    # 间隔设定
    alist = alist.copy()
    sublistcount = len(alist)//2
    while sublistcount > 0:
        # 子列表排序
        for startposition in range(sublistcount):
            gapInsertionSort(alist,startposition,sublistcount)
        print("After increments of size",sublistcount,"The list is",alist)
        print("sublistcount:{}".format(sublistcount))
        sublistcount = sublistcount //2

    return alist


def gapInsertionSort(alist,start,gap):
    print("input:",[alist[i] for i in list(range(start, len(alist), gap))])
    for index in range(start+gap,len(alist),gap):
        # 新项,插入项
        currentvalue = alist[index]
        position = index
        # 比对,移动
        while position >= gap and alist[position-gap] > currentvalue:
            alist[position] = alist[position-gap]
            position = position-gap
        # 插入新项
        alist[position] = currentvalue
    return alist

小结

谢尔排序是在插入排序的基础上实现的,从代码中可以显著的发现该特征。

归并排序

算法思路简述

归并排序使用了“分治策略”,归并排序是递归算法,思路是将数据表持续分裂为两半,对两半分别进行归并排序。

自己实现版

def mergeSort02(numList):
    # 基本结束条件
    numList = numList.copy()
    print("input:",numList)
    print("b left:",numList[:len(numList) // 2])
    print("b right:",numList[len(numList) // 2:])
    if len(numList) == 1:
        return numList
    # 缩小规模和调用自身
    else:
        left = mergeSort02(numList[:len(numList) // 2])
        print("left--{}:".format(left))
        right = mergeSort02(numList[len(numList) // 2:])
        print("right--{}:".format(right))
        # 关键:归并
        i, j, k = 0, 0, 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                numList[k] = left[i]
                i = i + 1
                k = k + 1
            else:
                numList[k] = right[j]
                j = j + 1
                k = k + 1
        while i < len(left):
            numList[k] = left[i]
            i = i + 1
            k = k + 1
        while j < len(right):
            numList[k] = right[j]
            j = j + 1
            k = k + 1
        print("merge:{}".format(numList))
        return numList

老师官方版

def mergeSort(lst):
    # 递归结束条件
    if len(lst) <= 1:
        return lst

    # 分解问题,并递归调用
    middle = len(lst) // 2
    left = mergeSort(lst[:middle])
    right = mergeSort(lst[middle:])

    # 合并左右半部,完成排序
    merged = []
    while left and right:
        if left[0] <= right[0]:
            merged.append(left.pop(0))
        else:
            merged.append(right.pop(0))
    merged.extend(right if right else left)
    return merged

对比小结

  • 本来以为在昨天刚学习递归的基础上能够快速实现,但是实际书写时还是加了很多调试语句才成功,可见递归学习的难度。

  • 同时,在归并的书写上,由于我个人喜欢用“for”循环,起初的实现都非常麻烦,改用while循环后,控制力明显增强

  • 老师实现版本使用了进出栈实现归并的过程,简直大开眼界!

快速排序

算法思路简述

快速排序的思路是依据一个“中值”数据项来把数据表分为两半:小于中值的一半和大于中值的一半,然后每部分分别进行
快速排序(递归)。

老师官方版

def partition(alist, first, last):
    # 选定“中值”
    pivotvalue = alist[first]
    # 左右标初值
    leftmark = first + 1
    rightmark = last

    done = False

    while not done:
        # 向右移动左标
        while leftmark <= rightmark and \
                alist[leftmark] <= pivotvalue:
            leftmark = leftmark + 1
        # 向左移动右标
        while rightmark >= leftmark and \
                alist[rightmark] >= pivotvalue:
            rightmark = rightmark - 1
        # 两标想错就结束移动
        if rightmark < leftmark:
            done = True
        else:
            # 左右标的值相互交换
            alist[leftmark], alist[rightmark] = alist[rightmark], alist[leftmark]

    # 中值就位
    alist[first], alist[rightmark] = alist[rightmark], alist[first]

    return rightmark


def quickSortHelper(alist, first, last):
    if first < last:
        splitpoint = partition(alist, first, last)
        quickSortHelper(alist, first, splitpoint - 1)
        quickSortHelper(alist, splitpoint + 1, last)


def quickSort(alist):
    quickSortHelper(alist, 0, len(alist) - 1)
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

儒雅的钓翁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值