【JSデータ構造とアルゴリズム】ヒルソートとクイックソートのアルゴリズム実装

序文

いくつかの簡単な並べ替えアルゴリズムが以前に導入されました。高度な並べ替えアルゴリズムに正式に入る前に、挿入並べ替えの主なアイデアを確認しましょう:マークされた要素を抽出し、左側のデータはローカルに順序付けられたシーケンスを形成し、右はなし。順序付けられたシーケンス、次に無秩序なシーケンスの各要素が順序付けられたシーケンスの対​​応する位置に挿入されます。

テキスト

レビュー

そのようなシーケンスがある場合:
ここに画像の説明を挿入
最後の要素1が配置されると、次の構造になります:
ここに画像の説明を挿入
最後のパスで、すべての要素を移動して要素1を対応する位置に配置する必要があります。
ここに画像の説明を挿入
これが問題です。後ろにそれほど多くのステップを移動することなく、1を少し前に移動させる方法は?次に、ヒルソーティングを導入しました。

ヒルソート

ヒルソートの改善は、以前のソートアルゴリズムであるO(N 2の時間計算量を打ち破ります。

思想

Shell's Sortは一種の挿入ソートであり、「減少増分ソート」(Diminishing Increment Sort)とも呼ばれ、直接挿入ソートアルゴリズムのより効率的で改良されたバージョンです。

  • 最初のステップは、添え字の値を特定の増分(通常はN / 2、その後は連続して半分)にグループ化して、挿入ソートに従って各グループをソートすることです。
  • 2番目のステップでは、増分が徐々に減少し、各グループに含まれるキーワードが増えます。増分が1に減少すると、ファイル全体が1つのグループに分割され、アルゴリズムが終了します。


ここに画像の説明を挿入

適切な増分の選択
以前の調査では、いくつかのよく知られた増分シーケンスがあります。

  • ヒル原稿の増分シーケンス:ヒルでソートされた原稿の中で、初期間隔はN / 2にすることをお勧めし、各パスの間隔は2つに分割されます。たとえば、N = 100の配列では、増分間隔シーケンスは次のようになります。 :50、25、12、6、3、1
  • Hibbardインクリメンタルシーケンス:インクリメンタルアルゴリズムは2 k-1、つまり1、3、5、7 ...などです。最悪の複雑さはO(N 3/2)であり、平均の複雑さはO(N 5/4)であると推測されます
  • Sedgewickインクリメンタルシーケンス:インクリメンタルアルゴリズムは4 i -32 i + 1、つまり1,5,19,41,109 ...などです。
    最悪の複雑さはO(N 4/3)であり、平均の複雑さはO(N 7/6)です。

ヒバードインクリメンタルシーケンスとセッジウィックインクリメンタルシーケンスの複雑さはまだ確認されておらず、実装も比較的難しいため、ヒル原稿のインクリメンタルシーケンスを選択します。この方法では、ソートを開始する前に追加の計算作業は必要ありません。

ヒルソートアルゴリズムの実装

// 希尔排序
ArrayList.prototype.sellSort = function () {
    
    
    // 1.获取数组的长度
    var length = this.array.length;

    // 2.选择希尔排序的原稿增量,初始间距是N / 2
    var gap = Math.floor(length / 2);

    //3.让间隔gap不断的减小
    while (gap >= 1) {
    
    
        // 4.以grp作为间隔,进行分组,分组进行插入排序
        for (var i = gap; i < length; i++) {
    
    
            var temp = this.array[i];
            var j = i;
            while (this.array[j - gap] > temp && j > gap - 1) {
    
    
                this.array[j] = this.array[j - gap];
                j -= gap;
            }

            // 5.将j位置的元素赋值给temp
            this.array[j] = temp;

        }
        // 6.重新计算新的间隔
        gap = Math.floor(gap / 2);
    }
}

ヒルソーティング効率

時間計算量はO(N 2)です

ヒルソートは、挿入ソートの次の2つの特性に基づいて改善された方法を提案します。

  • 挿入ソートは、ほぼソートされたデータを操作するときに効率が高く、線形ソートの効率を実現できます。
  • ただし、挿入ソートは一度に1ビットしかデータを移動できないため、一般に挿入ソートは非効率的です。

クイックソート

前述のヒルソートは挿入ソートの改良版ですが、クイックソートはバブルソートのアップグレードバージョンです。実際、バブルソートよりもはるかに優れています。再帰を使用します。

思想

  • 最初のステップ:ベンチマークを決定します;
  • ステップ2:ベースラインの前にベースラインよりも小さいすべての数値を配置し、ベースラインの後にベースラインよりも大きいすべての数値を配置します(同じ数値をどちらの側にも配置できます)。このパーティションの後、参照はシーケンスの途中に配置されます。このプロセスはパーティション操作と呼ばれます。
  • 並べ替えが完了するまで、ベンチマークの前面と背面で1番目と2番目の手順を再帰的に実行します。

適切なベンチマークを選択する

    1. 部分シーケンスの最後のデータが参照として選択され、2つの移動ポインターが使用されます。フロントポインターの数がバックポインターの数よりも大きい場合、それらは交換されます。(操作は簡単ですが、代表的なものではありません)
    1. データ範囲のベンチマークとしてランダムインデックスが使用され、ベンチマークは部分シーケンスの最後に配置され、操作は最初の方法と同じです。(確率は同じですが、乱数を実装する必要があり、統一された操作は必要ありません)
    1. 部分シーケンスの最初、中央、最後の3つのデータを選択して中央値を並べ替え、中央値をベンチマークとして使用して、シーケンスの最後から2番目の位置を配置します。(代表的で統一された運用)

包括的に検討し、最終的に中央値を取る方法を選択します
ここに画像の説明を挿入


2を達成するためのクイックソートアルゴリズム、2つの数値を交換する

// 交换两个数
ArrayList.prototype.swap = function (a, b) {
    
    
    var temp = this.array[a];
    this.array[a] = this.array[b];
    this.array[b] = temp;
}

1.ベンチマークを決定します

// 确定基准
ArrayList.prototype.median = function (left, right) {
    
    
    // 1.求出中间的位置
    var mid = Math.floor((left + right) / 2);
    // 2.判断并且进行交换 三数的冒泡思想
    if (this.array[left] > this.array[mid]) this.swap(left, mid);
    if (this.array[left] > this.array[right]) this.swap(left, right);
    if (this.array[mid] > this.array[right]) this.swap(mid, right);
    // 3.巧妙的操作: 将mid移动到right - 1的位置.
    this.swap(mid, right - 1);
    // 4.返回pivot
    return this.array[right - 1];
}
// 快速排序
ArrayList.prototype.quickSort = function () {
    
    
    this.quick(0, this.array.length - 1);
}

// 内部递归使用
ArrayList.prototype.quick = function (left, right) {
    
    

    // 1.递归结束条件
    if (left >= right) return false;
    // 2.获取基准
    var pivot = this.median(left, right);

    // 3.定义变量,开始进行交换
    var i = left;
    var j = right - 1;
    while (i < j) {
    
    
        // 3.1 找到比基准值大的数停止
        while (i < right && this.array[++i] < pivot) {
    
     }
        // 3.2 找到比基准值小的数停止
        while (j > left && this.array[--j] > pivot) {
    
     }
        // 3.3 交换与否
        if (i < j) {
    
    
            this.swap(i, j);
        } else {
    
    
            break;
        }
    }
    // 4.将基准放在正确的位置
    this.swap(i, right - 1);
    // 5.递归的调用左边序列
    this.quick(left, i - 1);
    // 6.递归的调用右边序列
    this.quick(i + 1, right);
}

Baidu百科事典Lite版

const quickSort = (array) => {
    
    
    const sort = (arr, left = 0, right = arr.length - 1) => {
    
    
        if (left >= right) {
    
    //如果左边的索引大于等于右边的索引说明整理完毕
            return
        }
        let i = left
        let j = right
        const baseVal = arr[j] // 取无序数组最后一个数为基准值
        while (i < j) {
    
    //把所有比基准值小的数放在左边大的数放在右边
            while (i < j && arr[i] <= baseVal) {
    
     //找到一个比基准值大的数交换
                i++
            }
            arr[j] = arr[i] // 将较大的值放在右边如果没有比基准值大的数就是将自己赋值给自己(i 等于 j)
            while (j > i && arr[j] >= baseVal) {
    
     //找到一个比基准值小的数交换
                j--
            }
            arr[i] = arr[j] // 将较小的值放在左边如果没有找到比基准值小的数就是将自己赋值给自己(i 等于 j)
        }
        arr[j] = baseVal // 将基准值放至中央位置完成一次循环(这时候 j 等于 i )
        sort(arr, left, j - 1) // 将左边的无序数组重复上面的操作
        sort(arr, j + 1, right) // 将右边的无序数组重复上面的操作
    }
    const newArr = array.concat() // 为了保证这个函数是纯函数拷贝一次数组
    sort(newArr)
    return newArr
}

ヒルソーティング効率

時間計算量はO(N * logN)です

総括する

私はこれらの2つのより高速なソートアルゴリズムのみを学習しました。バケットソートとマージソートはまだ学習していません。学習したら共有します。一緒に来てくださいヾ(◍°∇°◍)ノ゚。

おすすめ

転載: blog.csdn.net/weixin_42339197/article/details/102950030