[Structure de données et algorithme JS] Implémentation de l'algorithme du tri de Hill et du tri rapide

Préface

Plusieurs algorithmes de tri simples ont été introduits auparavant. Avant d'entrer formellement l'algorithme de tri avancé, passons en revue l'idée principale du tri par insertion: extraire un élément marqué, les données de gauche forment une séquence ordonnée localement, et les données sur le droite est aucun. Séquence ordonnée, puis chaque élément de la séquence désordonnée est inséré dans la position correspondante de la séquence ordonnée.

texte

revoir

S'il y a une telle séquence:
Insérez la description de l'image ici
lorsque le dernier élément 1 est agencé, il a la structure suivante:
Insérez la description de l'image ici
alors dans la dernière passe, il faut déplacer tous les éléments pour mettre l'élément 1 dans la position correspondante,
Insérez la description de l'image ici
c'est le problème. Comment faire avancer un peu sans avoir à faire autant de pas en arrière? Puis introduit le tri Hill.

Tri de colline

L'amélioration du tri de Hill brise la complexité temporelle de l'algorithme de tri précédent, qui est O (N 2 ).

pensait

Le tri de Shell est une sorte de tri par insertion, également connu sous le nom de "tri incrémental décroissant" (tri par incrément décroissant), qui est une version plus efficace et améliorée de l'algorithme de tri par insertion directe.

  • La première étape consiste à regrouper les valeurs d'indice en certains incréments (généralement N / 2, et à les diviser par deux par la suite) pour trier chaque groupe en fonction du tri par insertion.
  • Dans la deuxième étape, l'incrément est progressivement réduit et chaque groupe contient de plus en plus de mots-clés. Lorsque l'incrément est réduit à 1, le fichier entier est divisé en un seul groupe et l'algorithme est arrêté.

Exemple
Insérez la description de l'image ici

Choix du bon incrément
Dans les explorations précédentes, il existe plusieurs séquences d'incrémentation bien connues.

  • Séquence incrémentielle des manuscrits de Hill : parmi les manuscrits triés par Hill, l'espacement initial est recommandé d'être N / 2 et l'espacement de chaque passage est divisé en deux moitiés. Par exemple, dans un tableau de N = 100, la séquence d'espacement incrémentiel sera : 50,25,12,6,3,1
  • Séquence incrémentale Hibbard : l'algorithme incrémental est 2 k-1 , qui vaut 1 , 3, 5, 7 ... etc. La pire complexité est O (N 3/2 ), et la complexité moyenne est supposée être O (N 5/4 ).
  • Séquence incrémentale de Sedgewick : l'algorithme incrémental est 4 i -32 i + 1, soit 1,5,19,41,109 ... etc.
    La pire complexité est O (N 4/3 ) et la complexité moyenne est O (N 7/6 ).

La complexité de la séquence incrémentale de Hibbard et de la séquence incrémentale de Sedgewick n'a pas encore été confirmée, et elle est également relativement difficile à mettre en œuvre. Par conséquent, nous choisissons la séquence incrémentale du manuscrit de Hill. Cette méthode ne nécessite pas de travail de calcul supplémentaire avant de commencer le tri.

Implémentation de l'algorithme de tri de Hill

// 希尔排序
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);
    }
}

Efficacité du tri en colline

La complexité temporelle est O (N 2 )

Hill sorting propose une méthode améliorée basée sur les deux propriétés suivantes du tri par insertion:

  • Le tri par insertion a une efficacité élevée lorsqu'il fonctionne sur des données qui ont presque été triées, c'est-à-dire qu'il peut atteindre l'efficacité du tri linéaire.
  • Mais le tri par insertion est généralement inefficace, car le tri par insertion ne peut déplacer les données qu'un bit à la fois.

Tri rapide

Le tri Hill mentionné précédemment est une amélioration du tri par insertion, tandis que le tri rapide est une version améliorée du tri à bulles. En fait, il est bien meilleur que le tri à bulles. Il utilise la récursivité.

pensait

  • La première étape: déterminer un repère ;
  • Étape 2: Mettez tous les nombres plus petits que la ligne de base avant la ligne de base et tous les nombres plus grands que la ligne de base après la ligne de base (les mêmes nombres peuvent être placés de chaque côté). Après cette partition, la référence se trouve au milieu de la séquence. Ce processus est appelé opération de partition.
  • Effectuez de manière récursive les première et deuxième étapes à l'avant et à l'arrière du repère jusqu'à ce que le tri soit terminé.

Choisissez la bonne référence

    1. La dernière donnée de la séquence partielle est sélectionnée comme référence, puis deux pointeurs mobiles sont utilisés. Si le numéro du pointeur avant est supérieur au numéro du pointeur arrière, ils sont échangés. (L'opération est simple, mais pas représentative)
    1. Un indice aléatoire est utilisé comme repère dans la plage de données , et le repère est placé à la fin de la séquence partielle, et l'opération est la même que la première méthode. (La probabilité est égale, mais des nombres aléatoires doivent être implémentés, pas d'opération unifiée)
    1. Sélectionnez les trois premières, moyennes et dernières données de la séquence partielle pour trier la médiane et utilisez la médiane comme repère pour placer l'avant-dernière position dans la séquence. (Représentant, opération unifiée)

Une réflexion approfondie, nous choisissons enfin la méthode de prise de
Insérez la description de l'image ici

Algorithme de tri rapide pour atteindre
2, échanger deux nombres

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

1. Déterminez la référence

// 确定基准
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);
}

Encyclopédie Baidu Édition 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
}

Efficacité du tri en colline

La complexité temporelle est O (N * logN)

Pour résumer

Je n'ai appris que ces deux algorithmes de tri plus rapides. Je n'ai pas encore appris le tri par seau et le tri par fusion. Je les partagerai quand je les aurai appris. Venez ensemble ヾ (◍ ° ∇ ° ◍) ノ ゙.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_42339197/article/details/102950030
conseillé
Classement