Python を使用して 10 の古典的な並べ替えアルゴリズムを実装する (アニメーション付き)

ソート アルゴリズムは、データ構造とアルゴリズムで最も基本的なアルゴリズムの 1 つです。

ソート・アルゴリズムは、内部ソートと外部ソートに分けることができます. 内部ソートとは、データ・レコードがメモリー内でソートされることを意味し、外部ソートとは、ソートされたデータが大きすぎてすべてのソート・レコードを一度に収容できないことを意味し、外部ストレージが必要になることを意味します.ソートプロセス中にアクセスされます。一般的な内部ソート アルゴリズムは、挿入ソート、ヒル ソート、選択ソート、バブル ソート、マージ ソート、クイック ソート、ヒープ ソート、基数ソートなどです。写真で要約すると:

時間計算量について

  1. 正方順 (O(n2)) ソート さまざまなタイプの単純なソート: 直接挿入、直接選択、およびバブル ソート。

  2. 線形対数順 (O(nlog2n)) によるクイック ソート、ヒープ ソート、マージ ソート。

  3. O(n1+§)) 順序付け、§ は 0 と 1 の間の定数です。ヒルソート

  4. バケットおよびボックスの並べ替えに加えて、線形順序 (O(n)) の基数並べ替え。

安定性について

  • ソート後の 2 つの等しいキーの順序は、ソート前の順序と同じです

  • 安定したソート アルゴリズム: バブル ソート、挿入ソート、マージ ソート、および基数ソート。

  • 安定していないソート アルゴリズム: 選択ソート、クイック ソート、ヒル ソート、ヒープ ソート。

用語集

  • n: データサイズ

  • k: 「バケット」の数

  • インプレース: 一定のメモリを占有し、追加のメモリを占有しません

  • アウトプレース: 余分なメモリを消費します

1. バブルソート

バブル ソート (Bubble Sort) もシンプルで直感的な並べ替えアルゴリズムです。一度に 2 つの要素を比較し、順序が間違っている場合はそれらを交換しながら、並べ替え対象の配列を繰り返し処理します。シーケンスを訪問する作業は、交換の必要がなくなるまで、つまり、シーケンスがソートされるまで繰り返されます。このアルゴリズムの名前は、小さい要素が交換によってシーケンスの先頭にゆっくりと「浮かび上がる」という事実に由来しています。

最もシンプルなソートアルゴリズムの一つであるバブルソートは、単語帳に出てきたAbandonと同じ感覚で、毎回最初のページで最初に出てくるので、一番馴染み深いです。バブルソートには、フラグを設定する最適化アルゴリズムもあります. シーケンストラバーサル中に要素が交換されない場合、シーケンスがすでに順序付けられていることが証明されます. しかし、この改善はパフォーマンスの改善にはあまり役立ちません。

(1) アルゴリズムステップ

  1. 隣接する要素を比較します。1 番目が 2 番目よりも大きい場合は、両方を交換します。

  2. 最初の最初のペアから最後の最後のペアまで、隣接する要素の各ペアに対して同じことを行います。このステップが完了すると、最後の要素が最大数になります。

  3. 最後の要素を除くすべての要素について、上記の手順を繰り返します。

  4. 比較する数値のペアがなくなるまで、要素を徐々に減らして上記の手順を繰り返します。

(2) アニメーションのプレゼンテーション

(3) Python コード

def bubbleSort(arr):
    for i in range(1, len(arr)):
        for j in range(0, len(arr)-i):
            if arr[j] > arr[j+1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
    return arr

2. 選択ソート

選択ソートは、単純で直感的なソート アルゴリズムです。どのようなデータが入っても、O(n²) の時間計算量があります。したがって、使用する場合は、データサイズが小さいほど良いです。唯一の利点は、追加のメモリ領域を占有しないことです。

(1) アルゴリズムステップ

  1. 最初に、ソートされていないシーケンスで最小 (最大) の要素を見つけ、ソートされたシーケンスの先頭に格納します

  2. 次に、並べ替えられていない残りの要素から最小 (最大) の要素を探し続け、並べ替えられたシーケンスの最後に配置します。

  3. すべての要素が並べ替えられるまで、2 番目の手順を繰り返します。

(2) アニメーションのプレゼンテーション

(3) Python コード

def selectionSort(arr):
    for i in range(len(arr) - 1):
        # 记录最小数的索引
        minIndex = i
        for j in range(i + 1, len(arr)):
            if arr[j] < arr[minIndex]:
                minIndex = j
        # i 不是最小数时,将 i 和最小数进行交换
        if i != minIndex:
            arr[i], arr[minIndex] = arr[minIndex], arr[i]
    return arr

3. 挿入ソート

挿入ソートのコード実装は、バブル ソートや選択ソートほど単純でも粗雑でもありませんが、ポーカーをプレイしたことがある人なら誰でも数秒で理解できるはずなので、その原理は最も理解しやすいはずです。挿入ソートは、最もシンプルで直感的なソート アルゴリズムです. 順序付けられたシーケンスを構築することによって機能します. ソートされていないデータについては、ソートされたシーケンスの後ろから前にスキャンし、対応する位置を見つけて挿入します.

バブルソートと同様に、挿入ソートにもスプリットハーフ挿入と呼ばれる最適化アルゴリズムがあります。

(1) アルゴリズムステップ

  1. ソートする最初のシーケンスの最初の要素を順序付けられたシーケンスとして扱い、2 番目の要素から最後の要素までをソートされていないシーケンスとして扱います。

  2. ソートされていないシーケンスを最初から最後まで順番にスキャンし、スキャンされた各要素をソート済みシーケンスの適切な位置に挿入します。(挿入される要素が順序付けられたシーケンス内の要素と等しい場合、挿入される要素は等しい要素の後に挿入されます。)

(2) アニメーションのプレゼンテーション

(3) Python コード

def insertionSort(arr):
    for i in range(len(arr)):
        preIndex = i-1
        current = arr[i]
        while preIndex >= 0 and arr[preIndex] > current:
            arr[preIndex+1] = arr[preIndex]
            preIndex-=1
        arr[preIndex+1] = current
    return arr

4.ヒルソート

ヒル ソート (デクリメント インクリメント ソート アルゴリズムとも呼ばれます) は、挿入ソートのより効率的で改良されたバージョンです。しかし、ヒル ソートは不安定なソート アルゴリズムです。

ヒル ソートは、挿入ソートの次の 2 つのプロパティに基づく改良された方法です。

  • 挿入ソートは、ほぼソートされたデータを操作する場合に非常に効率的です。つまり、線形ソートの効率を達成できます。

  • ただし、挿入ソートは一度に 1 ビットしかデータを移動できないため、通常、挿入ソートは非効率的です。

ヒルソートの基本的な考え方は、まずソート対象のレコード列全体を直接挿入ソート用のいくつかのサブシーケンスに分割し、シーケンス全体のレコードが「基本的に順序が整っている」場合に、すべてのレコードに対して直接挿入ソートを実行することです。順番に。

(1) アルゴリズムステップ

  1. 増分シーケンス t1、t2、...、tk を選択します。ここで、ti > tj、tk = 1 です。

  2. 増分シーケンス番号 k に従って、シーケンスを k 回ソートします。

  3. ソートごとに、対応するインクリメント ti に従って、ソート対象の列が長さ m のいくつかのサブシーケンスに分割され、各サブリストに対して直接挿入ソートがそれぞれ実行されます。増分係数が 1 の場合のみ、シーケンス全体がテーブルとして扱われ、テーブルの長さはシーケンス全体の長さになります。

(2) Python コード

def shellSort(arr):
    import math
    gap=1
    while(gap < len(arr)/3):
        gap = gap*3+1
    while gap > 0:
        for i in range(gap,len(arr)):
            temp = arr[i]
            j = i-gap
            while j >=0 and arr[j] > temp:
                arr[j+gap]=arr[j]
                j-=gap
            arr[j+gap] = temp
        gap = math.floor(gap/3)
    return arr

5.マージソート

マージソート(Merge sort)は、マージ操作に基づく効果的なソートアルゴリズムです。このアルゴリズムは、分割統治法の非常に典型的なアプリケーションです。

分割統治の典型的なアルゴリズム アプリケーションとして、マージ ソートを実装するには 2 つの方法があります。

  • トップダウン再帰 (すべての再帰メソッドは繰り返しで書き直すことができるため、2 つ目のメソッドがあります);

  • ボトムアップ反復;

選択ソートと同様に、マージ ソートのパフォーマンスは入力データの影響を受けませんが、常に O(nlogn) 時間の計算量であるため、パフォーマンスは選択ソートよりもはるかに優れています。コストは、追加のメモリ領域が必要になることです。

(1) アルゴリズムステップ

  1. そのサイズがマージされたシーケンスを格納するために使用される 2 つの並べ替えられたシーケンスの合計になるように、スペースを適用します。

  2. 2 つのポインターを設定します。初期位置はそれぞれ、2 つの並べ替えられたシーケンスの開始位置です。

  3. 2 つのポインターが指す要素を比較し、比較的小さい要素を選択してマージ スペースに配置し、ポインターを次の位置に移動します。

  4. ポインターがシーケンスの最後に到達するまで、手順 3 を繰り返します。

  5. 別のシーケンスの残りのすべての要素を、マージされたシーケンスの末尾に直接コピーします。

(2) アニメーションのプレゼンテーション

(3) Python コード

def mergeSort(arr):
    import math
    if(len(arr)<2):
        return arr
    middle = math.floor(len(arr)/2)
    left, right = arr[0:middle], arr[middle:]
    return merge(mergeSort(left), mergeSort(right))

def merge(left,right):
    result = []
    while left and right:
        if left[0] <= right[0]:
            result.append(left.pop(0));
        else:
            result.append(right.pop(0));
    while left:
        result.append(left.pop(0));
    while right:
        result.append(right.pop(0));
    return result

6.クイックソート

Quicksort は、Tony Hall によって開発された並べ替えアルゴリズムです。平均して、n 個の項目を並べ替えるには O(nlogn) 回の比較が必要です。最悪の場合、Ο(n2) 回の比較が必要になりますが、これは一般的ではありません。実際、ほとんどのアーキテクチャで内部ループを効率的に実装できるため、通常、クイックソートは他の Ο(nlogn) アルゴリズムよりも大幅に高速です。

クイックソートは分割統治戦略を使用して、リストを 2 つのサブリストに分割します。

クイック ソートは、ソート アルゴリズムにおける分割統治の考え方のもう 1 つの典型的なアプリケーションです。本質的に、クイックソートは、バブルソートに基づく再帰的な分割統治法と見なす必要があります。

クイック ソートの名前は単純で失礼ですが、名前を聞くとすぐにその存在の意味がわかり、高速で効率的です。これは、ビッグ データの最速の並べ替えアルゴリズムの 1 つです。ワースト ケースの時間計算量は O(n²) に達しますが、優れています. ほとんどの場合、平均時間計算量が O(n logn) の並べ替えアルゴリズムよりも優れたパフォーマンスを発揮しますが、その理由は? 私にもわかりません. 幸いなことに、私の強迫性障害は再発しました. N 個以上の資料を調べた後、「アルゴリズム アートと情報学コンペティション」で満足のいく答えを見つけました。

連続配列のクイックソートなど、クイックソートの最悪の実行ケースは O(n²) です。しかし、その償却予想時間は O(nlogn) であり、O(nlogn) 表記で暗示された定数係数は小さく、複雑さが O(nlogn) で安定しているマージソートよりもはるかに小さいです。したがって、順序が弱い大多数の乱数シーケンスでは、マージ ソートよりもクイック ソートの方が常に優れています。

(1) アルゴリズムステップ

①「pivot」(ピボット)と呼ばれるシーケンスから要素を選択します。

②順番を入れ替えると、基準値より小さい要素は基準値の前に、基準値より大きい要素はすべて基準値の後ろに配置されます(同じ番号はどちらの側にも配置できます)。このパーティションが終了すると、ベンチマークはシーケンスの途中になります。これはパーティション操作と呼ばれます。

③ 基準値より小さい要素の部分配列と基準値より大きい要素の部分配列を再帰的にソートする。

再帰の一番下のケースは、シーケンスのサイズが 0 または 1 の場合です。つまり、常にソートされています。再帰的に実行されていますが、このアルゴリズムは常に終了します。これは、反復 (反復) ごとに少なくとも 1 つの要素が最後の位置に配置されるためです。

(2) アニメーションのプレゼンテーション

(3) Python コード

def quickSort(arr, left=None, right=None):
    left = 0 if not isinstance(left,(int, float)) else left
    right = len(arr)-1 if not isinstance(right,(int, float)) else right
    if left < right:
        partitionIndex = partition(arr, left, right)
        quickSort(arr, left, partitionIndex-1)
        quickSort(arr, partitionIndex+1, right)
    return arr

def partition(arr, left, right):
    pivot = left
    index = pivot+1
    i = index
    while  i <= right:
        if arr[i] < arr[pivot]:
            swap(arr, i, index)
            index+=1
        i+=1
    swap(arr,pivot,index-1)
    return index-1

def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]

7.ヒープソート

ヒープソート (Heapsort) は、ヒープのデータ構造を使用して設計されたソート アルゴリズムを指します。スタッキングは、完全な二分木に近似する構造であり、同時にスタッキングの性質を満たします。つまり、子ノードのキー値またはインデックスは常に親ノードよりも小さい (または大きい) ということです。ヒープソートは、ヒープの概念を利用してソートする選択ソートと言えます。次の 2 つの方法に分けられます。

  1. ラージ トップ ヒープ: 各ノードの値は、その子ノードの値以上であり、ヒープ ソート アルゴリズムで昇順で使用されます。

  2. 小さなトップ ヒープ: 各ノードの値は、ヒープ ソート アルゴリズムで降順で使用される子ノードの値以下です。

ヒープソートの平均時間計算量は O(nlogn) です。

(1) アルゴリズムステップ

  1. ヒープ H[0...n-1] を作成します。

  2. ヒープ ヘッド (最大値) とヒープ テールを入れ替えます。

  3. ヒープのサイズを 1 減らし、shift_down(0) を呼び出します。目的は、新しい配列の先頭データを対応する位置に調整することです。

  4. ヒープのサイズが 1 になるまで手順 2 を繰り返します。

(2) アニメーションのプレゼンテーション

(3) Python コード

def buildMaxHeap(arr):
    import math
    for i in range(math.floor(len(arr)/2),-1,-1):
        heapify(arr,i)

def heapify(arr, i):
    left = 2*i+1
    right = 2*i+2
    largest = i
    if left < arrLen and arr[left] > arr[largest]:
        largest = left
    if right < arrLen and arr[right] > arr[largest]:
        largest = right

    if largest != i:
        swap(arr, i, largest)
        heapify(arr, largest)

def swap(arr, i, j):
    arr[i], arr[j] = arr[j], arr[i]

def heapSort(arr):
    global arrLen
    arrLen = len(arr)
    buildMaxHeap(arr)
    for i in range(len(arr)-1,0,-1):
        swap(arr,0,i)
        arrLen -=1
        heapify(arr, 0)
    return arr

8. カウントとソート

カウントソートの核心は、入力データ値をキーに変換し、追加の配列空間に格納することです。一種の線形時間複雑度として、ソートのカウントでは、入力データが特定の範囲の整数でなければなりません。

(1) アニメーションのプレゼンテーション

(2) Python コード

def countingSort(arr, maxValue):
    bucketLen = maxValue+1
    bucket = [0]*bucketLen
    sortedIndex =0
    arrLen = len(arr)
    for i in range(arrLen):
        if not bucket[arr[i]]:
            bucket[arr[i]]=0
        bucket[arr[i]]+=1
    for j in range(bucketLen):
        while bucket[j]>0:
            arr[sortedIndex] = j
            sortedIndex+=1
            bucket[j]-=1
    return arr

9.バケットソート

バケット ソートは、カウンティング ソートのアップグレード バージョンです。これは関数の写像関係を利用したもので、高効率の鍵はこの写像関数の決定にあります。バケットの並べ替えを効率的にするには、次の 2 つのことを行う必要があります。

  1. 十分なスペースがある場合は、バケットの数を増やしてみてください

  2. 使用されるマッピング関数は、入力 N データを K バケットに均等に分散できます

同時に、バケット内の要素の並べ替えでは、比較並べ替えアルゴリズムの選択がパフォーマンスにとって重要です。

最速はいつですか

入力データを各バケットに均等に分散できる場合。

一番遅いのはい​​つですか

入力データが同じバケットに割り当てられている場合。

Python コード

def bucket_sort(s):
    """桶排序"""
    min_num = min(s)
    max_num = max(s)
    # 桶的大小
    bucket_range = (max_num-min_num) / len(s)
    # 桶数组
    count_list = [ [] for i in range(len(s) + 1)]
    # 向桶数组填数
    for i in s:
        count_list[int((i-min_num)//bucket_range)].append(i)
    s.clear()
    # 回填,这里桶内部排序直接调用了sorted
    for i in count_list:
        for j in sorted(i):
            s.append(j)

if __name__ == __main__ :
    a = [3.2,6,8,4,2,6,7,3]
    bucket_sort(a)
    print(a) # [2, 3, 3.2, 4, 6, 6, 7, 8]

10.基数ソート

基数ソートは非比較整数ソート アルゴリズムで、その原理は、整数を桁ごとに異なる数に分割し、各桁を個別に比較することです。整数は特定の形式の文字列 (名前や日付など) や浮動小数点数も表すことができるため、基数の並べ替えは整数に限定されません。

基数ソート vs カウントソート vs バケットソート

基数ソートには 2 つの方法があります。

これら 3 つの並べ替えアルゴリズムはすべてバケットの概念を使用していますが、バケットの使用には明らかな違いがあります。

  • カーディナリティの並べ替え: キー値の各桁に従ってバケットが割り当てられます。

  • カウント ソート: 各バケットには 1 つのキー値のみが格納されます。

  • バケットの並べ替え: 各バケットには特定の範囲の値が格納されます。

アニメーションのプレゼンテーション

Python コード

def RadixSort(list):
    i = 0                                    #初始为个位排序
    n = 1                                     #最小的位数置为1(包含0)
    max_num = max(list) #得到带排序数组中最大数
    while max_num > 10**n: #得到最大数是几位数
        n += 1
    while i < n:
        bucket = {} #用字典构建桶
        for x in range(10):
            bucket.setdefault(x, []) #将每个桶置空
        for x in list: #对每一位进行排序
            radix =int((x / (10**i)) % 10) #得到每位的基数
            bucket[radix].append(x) #将对应的数组元素加入到相 #应位基数的桶中
        j = 0
        for k in range(10):
            if len(bucket[k]) != 0: #若桶不为空
                for y in bucket[k]: #将该桶中每个元素
                    list[j] = y #放回到数组中
                    j += 1
        i += 1
return  list

おすすめ

転載: blog.csdn.net/veratata/article/details/128612229