Pythonのデータ構造とアルゴリズム:ソートアルゴリズムの概要

ダークホース プログラマ アルゴリズムの学習ノート

バブルソート

バブル ソートは最も基本的な交換ソートです。シーケンスを n-1 回走査し、シーケンスを走査するたびに、残りのシーケンスの最大値を取り出します。バブル ソートの一般的なプロセスは次のとおりです
。ループ、最初の層 for ループは、range(1, length) に設定されるトラバーサルの数を制御し、for ループの 2 番目の層はトラバーサル シーケンスの長さを制御します。各走査では最大の数がソートされる (終わりから始めにソートされる) ため、各走査で 2 番目の層で走査されるシーケンスの長さが制御され、時間の複雑さが軽減され、効率が向上します。
for ループの最初の層は走査数を制御し、
for ループの 2 番目の層は走査されるシーケンスの長さ、走査数カウントを制御します。シーケンスの長さは [0, length-count) です。各走査の最大値を選択します
。 、最後から 1 回 大きい順から小さい順にソートします
。 例: 元のデータ: [16, 8, 9, 25, 5]
最初の走査: count=1
走査されたシーケンスの長さ: [0, 5-count), length=5 (注) : 最後のインデックスは 3 で、インデックス 4 のデータが読み出されます。)
[8, 9, 16, 5, 25]
2 回目のトラバース: count=2
トラバースされたシーケンスの長さ: [0, 5-count) ,
[ 8, 9, 5, 16, 25 ] (注: 25はソートに参加しないことを意味します。つまり、length-count はソートに参加するシーケンスの長さの制御を実現します。ソート後は、ソートに参加するシーケンスはシーケンスに対応します (最大値)
3 回目のトラバース: count=3
トラバースされたシーケンスの長さ: [0, 5-count),
[8, 5, 9, 16, 25 ]
4 番目のトラバーサル: count=4
トラバースされたシーケンスの長さ: [0, 5-count),
[5, 8, 9, 16, 25 ]
結果: [5, 8, 9, 16, 25]

def bubble_sort(sequence: list):
    """
    冒泡排序
    每一次遍历将最大值放在最后,每一次遍历查找都是将找出最大数值,一个列表中,会遍历n-1次,
    :param sequence:
    :return:
    """
    length = len(sequence)
    if length < 2:  # length=0:sequence是一个空的;length=1:只有一个数值,不用排序
        return
    for count in range(1, length):  # count代表的是第几遍遍历sequence,需要遍历length-1次之后才会完成所有的元素的对比
        # 每次循环结束之后,sequence的最后一个元素是最大值
        change = False  # 序列有没有改变过顺序,False表示没有改表过顺序,True表示顺序改变过
        for index in range(0, length - count):  # 对sequence进行一次排序
            if sequence[index] > sequence[index + 1]:  # 前一个元素大于后一个元素
                # 二者交换位置
                sequence[index], sequence[index + 1] = sequence[index + 1], sequence[index]
                change = True  #
        if not change:
            break

選択ソート

n-1 回トラバースし、毎回最小値に対応するインデックスを選択します。トラバース後、データ交換を実行します。交換されたデータは
カウント トラバーサルです。つまり、インデックスは count に対応する要素と、最小値。

# =========================================================================#
# 选择排序,每次循环都是选择序列中的最小值,
# 每次选择的是最小值的时候,排序算法是稳定的;
# 每次选择的是最大值的时候,排序算法是不稳定的
# =========================================================================#
def select_sort(sequence):
    """
    选择排序
    原理:第一次遍历找到所索引范围0-length-1的最小值放在索引0,第二次找到索引范围1-length-1的最小值放在索引1处,,,
    通过list[count], list[min_value_index] = list[min_value_index], list[count]交换数据
    :param sequence:
    :return:
    """
    length = len(sequence)
    if length < 2:
        return

    # 操作索引更方便
    # 使用count控制遍历此时,每一次新的遍历开始,寻找最小值的序列都会少一个长度,n-1次

    for count in range(1, length):  # count:1, 2, 3, ... , length - 1
        start_index = count - 1  # 开始的索引的位置
        min_value_index = start_index  # 用来记录最小值对应的索引,每一次遍历,count都会比上一次+1

        change = False# 需要不需要交换至
        for index in range(count, length):  # 遍历所有值,
            """
            每次循环,都会是将最小值移动到最左侧,
            下一次遍历时会将已经排序好的部分隔离出去,只遍历没有排序的部分
            """
            if sequence[index] < sequence[min_value_index]:  # 找最小值对应的下标
                min_value_index = index
                change = True
        # 将最小值放在剩余序列的开头
        if change:  # 如
            sequence[start_index], sequence[min_value_index] = sequence[min_value_index], sequence[start_index]

挿入ソート

インデックス 1 からインデックス n-1 まで走査し、count 回目の走査は、index=count を境界として、左側が順序付けられた部分、右側が順序付けされていない部分となり、index=count と の値を走査して比較することで
、順序付けられた部分、対応する位置のインデックスを見つけます、

#  ======================================================================================#
#  插入算法,是一种简单直观的排序算法,
#  工作原理是通过构建有序序列,对于未排序的数据,在已排序序列中从后向前扫描,找到相应的位置并插入。
#  插入排序在实现上,在从后向前扫描过程中,需要反复把已排序的元素逐步向后挪位,为新的元素提供插入空间。
#  最优时间复杂度:O(n)
#  最坏时间复杂度O(n^2)
#  稳定性:稳定
#  ======================================================================================#
def insertion_sort(sequence: list):
    length = len(sequence)  # 序列长度
    # 从右边的无需序列中取出第几个元素执行线面的过程
    # 划分:左边是有序序列,右边是无序序列,每次遍历都是从右边取出一个元素,通过在左边做对比,将元素放在对应的位置上
    for count in range(1, length):  # count代表的是遍历的次数,取值范围1~n-1,也是每次遍历要将比较的数值的索引
        # 对第count个元素进行对比插入到左边有序的序列中
        # 下面遍历作用是将第count个元素在左边有序部分找到对应的位置
        for index in range(count, 0, -1):  # 将索引为count的数值和count, ..., 1的数值全部比对,
            if sequence[index] < sequence[index - 1]:  # 如果满足数值前移的条件,就将index-1对应的数值移动到index
                sequence[index - 1], sequence[index] = sequence[index], sequence[index - 1]  # 交换数据
            else:  # 不需要交换的意思就是:原序列的第count个元素在左边有序部分找到了它的对应的位置
                break

ヒルソート

ヒルソートは挿入アルゴリズムの改良版であり、増分によってシーケンスのグループ化を制御し、最終的にソート効果を実現します。

#  =============================================================================================#
#  希尔排序:插入排序的一种,也称缩小增量排序,是直接排序算法的一中更高效的改进版本。
#  希尔排序是非稳定的排序算法,希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;
#  随着增量的减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法终止。
#  gap的取值是需要数学计算的,通过计算才能确定最高效的gap的取值
#  稳定性:不稳定
#  =============================================================================================#
def shell_sort(sequence: list):
    length = len(sequence)
    if length < 2:  # 空列表或者只有一个元素的列表是不需要排序的
        return

    gap = length // 2
    while gap > 0:  # 控制gap的步长
        # 插入排序
        for count in range(gap, length):
            for index in range(count, 0, -gap):
                if sequence[index] < sequence[index - gap]:  # 前一个比后一个的值大
                    sequence[index - gap], sequence[index] = sequence[index], sequence[index - gap]
                else:
                    break
        gap = gap // 2  # 缩短gap步长

クイックソート (マスターが必要なソートアルゴリズム)

シーケンスから要素を「ベースライン」として選択し、最後に参照以下の左側のシーケンスの要素と、参照より大きい右側のシーケンスの要素をトラバーサルによって取得し、左側のサブシーケンスとその要素をすばやく並べ替えます。再帰的アルゴリズムによる正しいサブシーケンス

#  ===================================================================================================================#
#  快速排序,又称划分交换排序,通过一趟排序将要排序的数据分割成独立的两部分,
#  其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对两部分数据分别进行快速排序,
#  整个排序过程可以递归进行,以此达到整个数据换成有序序列。
#  步骤为  1.从数列中挑选出一个元素,称为基准;
#         2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准后面(相同的数可以放在任一边),
#         在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区操作;
#         3.递归把小于基准值元素的子数列和大于基准值元素的子数列排序
#  最优时间复杂度:O(n*log(n))
#  最坏时间复杂度:O(n^2)
#  稳定性:不稳定
#  ===================================================================================================================#
def quick_sort(sequence: list, first, last):
    # 只有一个元素,low=0时,不满足条件时退出执行,相当于对序列不进行任何操作,
    # 当first=last时说明基准左侧只有一个元素,无需排序,
    # first=last+1时,说明基准的位置没有改变,没有元素排序
    if first >= last:
        return

    mid_value = sequence[first]  # 挑选基准,将基准挑选出来之后,相当于在序列中空余出一个位置,可以对对应位置进行赋值,不用数值交换
    low = first  # 值比mid_value小的值对应的索引
    high = last  # 值比mid_value大的值对应的索引

    # 寻找基准的位置索引,并将基准两侧按照左侧全是小于等于基准的,右侧全是大于基准的原则对序列进行排列
    # 每交换一次数值之后,low和high交替改变,交替条件是:有数值的交换
    while low < high:  # 循环结束之后,low = high
        # high左移
        while low < high and sequence[high] >= mid_value:  # 等号位于哪相等的元素放在哪边
            high -= 1
        # 跳出循环的有两个条件:low=high,说明找到了基准的位置;sequence[high]<mid_value(基准),需要交换值
        sequence[low] = sequence[high]  # sequence[high]<mid_value,交换元素

        # low右移
        while low < high and sequence[low] < mid_value:
            low += 1
        sequence[high] = sequence[low]

    # 循环结束之后,low=high,low和high指向的索引位置就是基准所在的位置
    sequence[low] = mid_value
    # 对基准左侧的子序列进行排序,当first=low时,说明基准是序列的最小值
    quick_sort(sequence, first, low - 1)
    # 对基准的右侧序列进行排序,当low=last时说明基准是序列中的最大值
    quick_sort(sequence, low + 1, last)

マージソート

マージソートでは、まずシーケンスを半分に分割し、次に再帰を使用して両側のシーケンスを別々にソートします。

#  ======================================================================================================#
#  归并排序:使用递归
#  归并排序是采用分治法的一个典型应用。归并排序的思想就是先递归分解数组,在合并数组。
#  将数组分解最小之后,人后合并两个有序数组,基本思想是比较两个数组的最前面的数,谁小就先取谁,
#  取了后相应的指针就往后移一位。人后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
#  最优时间复杂度:O(n*log(n))
#  最坏时间复杂度:O(n*log(n))
#  稳定性:稳定
#  做为对比的话:归并排序时间复杂度最小,但是空间复杂度是最大的,因为是需要一个新的列表来保存输出,需要新的内存空间
#  ======================================================================================================#
def merge_sort(sequence: list) -> list:
    # 拆分过程,将一个序列对半拆分,奇数右边比左边多
    length = len(sequence)

    # 退出递归的条件,只有一个元素的序列是不需要排序的
    if length <= 1:
        return sequence

    """以下部分不一定能够执行到,当只有一个元素时,就没有必要拆分了,就会直接将列表返回"""
    mid = length // 2  # 拆分的中点

    # left 采用归并排序之后形成的有序的新的列表
    left_sequence = merge_sort(sequence[:mid])  # 输入是对列表的切片,所以输入是一个列表
    length_left = len(left_sequence)  # 长度
    # right 采用归并排序之后形成的有序的新的列表
    right_sequence = merge_sort(sequence[mid:])  # 输入是对列表的切片,所以输入是一个列表
    length_right = len(right_sequence)  # 长度

    # 合并元素merge(left, right),将两个有序的子序列合并成一个整体
    left_pointer = 0  # 左边子序列的指针
    right_pointer = 0  # 右边子序列的指针
    result = []  # 保存结果的列表

    # 循环控制合并元素,任何一个指针到头就会跳出循环,跳出循环时,另一个子序列还会有剩余元素,将剩余的元素直接加到result之后
    while left_pointer < length_left and right_pointer < length_right:  # 指针不能超过列表长度范围
        # 遍历对比两个子序列的值,小的先放在result列表中
        if left_sequence[left_pointer] <= right_sequence[right_pointer]:  # 感觉等于号放在这,算法是稳定的
            result.append(left_sequence[left_pointer])
            left_pointer += 1

        else:
            result.append(right_sequence[right_pointer])
            right_pointer += 1

    # 将剩余部分的元素添加进result之后
    result += left_sequence[left_pointer:]
    result += right_sequence[right_pointer:]

    return result

二分探索

#  ========================================================================================================#
#  二分查找:操作对象必须是有序的,使用的对象只能是顺序表
#  ========================================================================================================#
def binary_search1(sequence: list, item):
    """
    递归方法
    :param sequence:
    :param item:
    :return: bool:True:找到了,False:没有找到
    """
    length = len(sequence)
    # 退出递归的条件
    if length > 0:
        mid = length // 2

        if sequence[mid] == item:
            return True
        elif item < sequence[mid]:  # 左边
            return binary_search1(sequence[:mid], item)
        else:  # 右边
            return binary_search1(sequence[mid + 1:], item)
    else:  # length = 0,说明没有在这个序列中查找到item
        return False


def binary_search2(sequence, item):
    """
    非递归方法
    :param sequence:
    :param item:
    :return:
    """
    length = len(sequence)
    first = 0
    last = length - 1

    # 跳出循环时,有两种情况:找到了返回的是True;没有找到
    while first <= last:
        mid = (first + last) // 2
        if sequence[mid] == item:
            return True
        elif item < sequence[mid]:  # 左边
            last = mid - 1
        else:  # 右边
            first = mid + 1
    return False  # 执行这一句时,说明没有找到item

おすすめ

転載: blog.csdn.net/weixin_50727642/article/details/122304029