快速排序算法与归并排序算法理解

本文详细介绍了数据结构中的两种经典排序算法——快速排序和归并排序。快速排序采用分治策略,通过选取基准并不断分割列表实现排序;归并排序则是先递归分割列表,然后合并成有序列表。两种算法各有特点,适用于不同的场景。文中给出了详细的步骤解释及Python实现代码,是学习数据结构的好资料。
摘要由CSDN通过智能技术生成

最近在学习《数据结构(Python语言描述)》第二版,算是自己接触的第一本数据结构的书籍,感觉内容很绕,也看不太懂,但是决定慢慢啃下来,以此篇开始记录自己解决的一些问题与心得,如有他人要进行此书学习,也可以做一个借鉴。

首先这两种算法都基于同一个思想“divide and conquer”,即分开他,然后克服解决掉小的,最终大的就解决了

快速排序算法

此算法最主要的就是基准(pivot)的理解。正如书中所言

把小于基准的元素移至基准的左边,大于基准的元素移至基准的右边。从而如果基准是最大值,那么他就在列表的最右边,相反,如果他是最小值,那么他就会在最左边。但无论基准在哪里,排序后,他的位置就是最终有序序列中的位置。

有了这个想法,基准可以选择在每个列表的中间位置。通过递归,将列表分为子列表,一个sublist就是小于基准的,另一个sublist就是大于基准的,这样一直递归,当sublist的大小小于2时,就结束了,此时已经将列表分好了。为什么这么神奇呢?最关键的是这句

但无论基准在哪里,排序后,他的位置就是最终有序序列中的位置。

就实现了和插入算法相类似的功能。基准可以取中间位置,很简单,但这个的关键在于,如何利用基准成功分割成子序列。根据书中,其使用了boundary即边界的概念。
具体操作如下:

  • 将基准与列表中最后一个元素交换(方便基准和列表内所有元素进行比较)
  • 构建边界(相当于数学题中的辅助线),用来分割开小于基准的与大于基准的元素。边界最开始选在第一个元素前面。
  • 从边界后的第一个元素开始进行扫描,如果元素小于pivot,那么就把他和边界后的元素进行对换,并把边界向右移动一个距离。(相当于把元素放到了边界后,即小的那一侧,并且把大的先放到另一侧,就可以后续再交换的可能,但我理解,不放过去,直接放到边界后面也可以,但是涉及到列表总长不能变,并且索引变了,就比较麻烦,不如直接把两个元素进行一个对调)
  • 结束后,把基准和边界后的第一个元素进行交换(此时,基准左侧就是小于他的,右侧就是大于他的)
  • (利用边界,左侧、右侧的就继续进行以上操作,而边界的元素,即基准就不需要动了,因为位置已经确定了,所以就分成了(left, middle-1), (left, middle +1),将middle略过)

具体代码附下:

def swap(lyst, left, right):
    temp = lyst[right]
    lyst[right] = lyst[left]
    lyst[left] = temp


def quicksort(lyst):
    quicksortHelper(lyst, 0, len(lyst) - 1)


def quicksortHelper(lyst, left, right):
    if left < right:
    # 由于pivot已经定好了,所以后续不需要再关心pivot这个位置
        pivotLocation = partition(lyst, left, right)
        quicksortHelper(lyst, left, pivotLocation - 1)
        quicksortHelper(lyst, pivotLocation + 1, right)


def partition(lyst, left, right):
    # 找到中心基准,并与最后一个元素交换
    mid = (left + right) // 2
    pivot = lyst[mid]
    lyst[mid] = lyst[right]
    lyst[right] = pivot
    # 构建基准,并进行扫描比较
    boundary = left
    for index in range(left, right):
        if lyst[index] < pivot:
            swap(lyst, boundary, index)
            boundary += 1
    # 将基准与边界后第一个元素换回来
    swap(lyst, boundary, right)
    return boundary


import random


def main(size=20, sort=quicksort):
    # lyst = []
    # for count in range(size):
    #     lyst.append(random.randint(1, size + 1))
    lyst = [12, 19, 17, 18, 14, 11, 15, 13, 16]
    print(lyst)
    sort(lyst)
    print(lyst)


if __name__ == "__main__":
    main()

归并算法

如果上述快速排序总结成一句话就是,将列表中每一个元素当作基准,最终确定每个基准,那么归并算法总结而言的话,就是,分割后递归地将子列表进行排序,然后合并成一个有序列表
该算法需要先设置一个Buffer,即缓冲区,来暂时收纳相关数据
其具体实现如下:

  • 将整个列表进行分割,如果列表为2的倍数,那么最终最后一层分割成2^k个元素,其中k为层数,如果不是2的倍数,那么就分不平均。
  • 将两个子列表的中各自的第一个元素设置索引指针,指向这个列表的头部。那么他们就是low,以及middle+1
  • 比较这两个头部,小的就拿出来放到Buffer里,对应元素所在列表指针进行右移。直到一个列表已经被取完了,那么剩下的那个列表就统一扔到Buffer里
  • 将Buffer里的元素重新复制到lyst里的位置

具体代码附下:

def mergeSort(lyst):
    # copyBuffer  temporary space needed during merge
    # 原文中的array是自己实现的,现在就随便定义了一个len大小的数组来代替
    copyBuffer = list(range(len(lyst)))
    mergesortHelper(lyst, copyBuffer, 0, len(lyst)-1)


def mergesortHelper(lyst, copyBuffer, left, right):
    # mid middle point of sublist
    if left < right:
        middle = (left + right) // 2
        mergesortHelper(lyst, copyBuffer, left, middle)
        mergesortHelper(lyst, copyBuffer, middle+1, right)
        merge(lyst, copyBuffer, left, middle, right)


def merge(lyst, copyBuffer, left, middle, right):
    # Initialize i1 and i2 to the first items in each sublist
    i1 = left
    i2 = middle + 1
    # 将每一个子列表中值插入copyBuffer中
    for i in range(left, right+1):
        # 如果最后左侧的列表被抽完了,就将 右侧列表放进Buffer里
        if i1 > middle:
            copyBuffer[i] = lyst[i2]
            i2 += 1
        # 如果最后右侧的列表被抽完了,就将左侧列表放进Buffer里
        elif i2 > right:
            copyBuffer[i] = lyst[i1]
            i1 += 1
        # 比较两个sublist里的头部大小,小的放进Buffer里
        elif lyst[i1] < lyst[i2]:
            copyBuffer[i] = lyst[i1]
            i1 += 1
        else:
            copyBuffer[i] = lyst[i2]
            i2 += 1
    # 最终把buffer里的元素复制回到lyst中
    for i in range(left, right+1):
        lyst[i] = copyBuffer[i]


def main(size=20, sort=mergeSort):
    # lyst = []
    # for count in range(size):
    #     lyst.append(random.randint(1, size + 1))
    lyst = [12, 19, 17, 18, 14, 11, 15, 13, 16]
    print(lyst)
    sort(lyst)
    print(lyst)


if __name__ == "__main__":
    main()

复杂度这块还没搞明白,后续搞明白了继续写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值