Java implémente des algorithmes de tri couramment utilisés (tri rapide, tri par fusion, tri par base)

Algorithmes de tri courants

insérez la description de l'image ici

tri rapide

1. Introduction

Quicksort est un algorithme de tri développé par Tony Hall. En moyenne, trier n éléments nécessite O(nlogn) comparaisons. Dans le pire des cas, des comparaisons Ο(n2) sont nécessaires, mais ce n'est pas courant. En fait, le tri rapide est généralement beaucoup plus rapide que les autres algorithmes Ο(nlogn) car sa boucle interne peut être implémentée efficacement sur la plupart des architectures.

Quicksort utilise une stratégie de division pour régner pour diviser une liste en deux sous-listes.

Le tri rapide est une autre application typique de l'idée de diviser pour mieux régner dans les algorithmes de tri. Essentiellement, le tri rapide doit être considéré comme une méthode récursive de division et de conquête basée sur le tri à bulles.

Le nom de Quick Sort est simple et grossier, car dès que vous entendez le nom, vous connaissez le sens de son existence, qui est rapide et efficace ! C'est l'un des algorithmes de tri les plus rapides pour le Big Data. Bien que la complexité temporelle des pires cas atteigne O(n²), ils sont excellents et, dans la plupart des cas, ils fonctionnent mieux que les algorithmes de tri avec une complexité temporelle moyenne de O(n logn).

Le pire cas de tri rapide est O(n²), comme le tri rapide de tableaux séquentiels. Mais son temps attendu amorti est O(nlogn), et le facteur constant impliqué dans la notation O(nlogn) est petit, ce qui est beaucoup plus petit que le tri par fusion dont la complexité est stable en O(nlogn). Par conséquent, pour la grande majorité des séquences de nombres aléatoires avec un ordre plus faible, le tri rapide est toujours préférable au tri par fusion.

2. Analyse de la pensée

L'algorithme de tri rapide implémente le tri à travers plusieurs comparaisons et échanges, et son processus de tri est le suivant :
(1) Tout d'abord, définissez une valeur de coupure et divisez le tableau en parties gauche et droite via la valeur de coupure.
(2) Collectez les données supérieures ou égales à la valeur de coupure à droite du tableau et collectez les données inférieures à la valeur de coupure à gauche du tableau. A ce moment, chaque élément de la partie gauche est inférieur à la valeur limite et chaque élément de la partie droite est supérieur ou égal à la valeur limite.
(3) Ensuite, les données gauche et droite peuvent être triées indépendamment. Pour les données du tableau sur le côté gauche, une valeur limite peut être prise pour diviser cette partie des données en parties gauche et droite, et la plus petite valeur est placée sur le côté gauche, et la plus grande valeur est placée sur le côté droit. Les données du tableau à droite peuvent également être traitées de la même manière.
(4) Répétez le processus ci-dessus, on peut voir qu'il s'agit d'une définition récursive. Après avoir trié la partie gauche par récursivité, triez récursivement l'ordre de la partie droite. Lorsque le tri des données dans les parties gauche et droite est terminé, le tri du tableau entier est également terminé.

3. Diagramme

3.1 début

Commencez respectivement par les deux extrémités du tableau arrdétection. Trouvez d'abord un nombre inférieur à 8 de droite à gauche, puis trouvez un nombre supérieur à 8 de gauche à droite, puis échangez-les. Ici, vous pouvez utiliser deux variables L (gauche) et R (droite), qui pointent respectivement vers l'extrême gauche et l'extrême droite de la séquence. Au début, laissez L pointer à l'extrême gauche du tableau, pointant vers le nombre 8. Laissez R pointer à l'extrême droite du tableau, pointant vers le nombre 11.
insérez la description de l'image ici

3.2 Rencontrer un nombre plus petit que le repère de droite à gauche

insérez la description de l'image ici

3.3 Arrêtez-vous à droite, cherchez un nombre supérieur au repère de gauche à droite

insérez la description de l'image ici

3.4 Échanger les nombres correspondant à L et R

insérez la description de l'image ici

après échange
insérez la description de l'image ici

3.5 Suite de la méthode ci-dessus

Le premier R commence à être expédié. Étant donné que le nombre de base défini ici est le nombre le plus à gauche, il est très important de laisser R aller en premier (veuillez réfléchir par vous-même pourquoi). La sentinelle R se déplace vers la gauche pas à pas (c'est-à-dire R–-) jusqu'à ce qu'elle trouve un nombre inférieur à 8 et s'arrête. Ensuite, la sentinelle L se déplace pas à pas vers la droite (c'est-à-dire L++) jusqu'à ce qu'elle trouve un nombre supérieur à 8 et s'arrête.

échange
insérez la description de l'image ici

après échange

insérez la description de l'image ici

A ce moment R continue de se déplacer vers la gauche
insérez la description de l'image ici

Alors L se déplace
insérez la description de l'image ici

Lorsque L == R, cela signifie que cette position est la position de 8, échange
insérez la description de l'image ici

obtenir:

insérez la description de l'image ici
Ceci conclut le premier cycle de détection. À ce moment, le numéro de référence 8 est utilisé comme point de division, les nombres à gauche de 8 sont tous inférieurs ou égaux à 8 et les nombres à droite de 8 sont tous supérieurs ou égaux à 8. En regardant en arrière le processus tout à l'heure, en fait, la mission de R est de trouver un nombre plus petit que le nombre de référence, et la mission de L est de trouver un nombre supérieur au nombre de référence jusqu'à ce que L et R se rencontrent.

3.6 Manipulation des chiffres à gauche

insérez la description de l'image ici

3.7 Traitement des nombres à droite

insérez la description de l'image ici

obtenir:
insérez la description de l'image ici

4. Mise en œuvre du code

/**
 * 快速排序
 * @author 尹稳健~
 * @version 1.0
 * @time 2022/9/10
 */
public class QuickSort {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    8, 12, 19, -1, 45, 0, 14, 4, 11};
        quickSort(arr,0,arr.length-1);
        for (int i = 0; i < arr.length; i++) {
    
    
            System.out.print(arr[i]+" ");
        }
    }
    public static void quickSort(int[] arr,int start, int end){
    
    
        // 什么时候结束排序?
        if (start>end){
    
    
            return;
        }
        // 左指针
        int L = start;
        // 右指针
        int R = end;
        // 基准
        int pivot = arr[start];
        // 只要L!=R就继续循环
        while (L < R){
    
    
            // 右边大于基准,从右向左移动
            while (L<R && arr[R] >= pivot){
    
    
                R --;
            }
            // 左边小于基准,从左向右移动
            while (L<R&& arr[L] <= pivot){
    
    
                L ++;
            }
            // 说明右边有小于基准的数,左边有大于基准的数,交换
            if (L<R){
    
    
                int temp = arr[L];
                arr[L] = arr[R];
                arr[R] = temp;
            }
        }
        // L 与 R碰面 找到了 基准的位置
        arr[start] = arr[L];
        arr[L] = pivot;

        // 左边排序
        quickSort(arr,start,R-1);
        // 右边排序
        quickSort(arr,L+1,end);

    }

}

Tri rapide Cet article est bien écrit, vous pouvez vous référer à l'écriture et à la compréhension du tri rapide d'autres personnes


tri par fusion

1. Introduction

Le tri par fusion est un algorithme de tri efficace et stable basé sur l'opération de fusion. Cet algorithme est une application très typique de diviser pour régner (Divide and Conquer). Combinez les sous-séquences ordonnées pour obtenir une séquence complètement ordonnée ; c'est-à-dire, faites d'abord chaque sous-séquence dans l'ordre, puis faites les segments de la sous-séquence dans l'ordre. La fusion de deux listes triées en une seule liste triée s'appelle une fusion bidirectionnelle.

2. Analyse de la pensée

Le tri par fusion utilise l'idée de diviser pour mieux régner, et la difficulté est de régner

  • Appliquer de l'espace pour que sa taille soit la somme des deux séquences triées, qui est utilisée pour stocker la séquence fusionnée
  • tout d'abordséquence de décomposition, décompose la séquence en séquences de chaque groupeLa quantité est 1, puis effectuez un tri par fusion
  • installationdeux pointeurs, les positions initiales sont les positions de départ des deux séquences triées
  • Comparez les éléments pointés par les deux pointeurs, sélectionnez l'élément relativement petit et placez-le dans l'espace de fusion, puis déplacez le pointeur vers la position suivante
  • Répétez l'étape précédente jusqu'à ce qu'un certain pointeur atteigne la fin de la séquence
  • Copiez tous les éléments restants d'une autre séquence directement à la fin de la séquence fusionnée

3. Diagramme

3.1 Diviser, décomposer toutes les séquences

insérez la description de l'image ici

3.2 Démarrer le tri et la fusion

insérez la description de l'image ici

Après chaque remplissage, le pointeur se déplace d'un bit
insérez la description de l'image ici

insérez la description de l'image ici

Lorsque le tableau de gauche n'a pas d'éléments, le droit est directement rempli
insérez la description de l'image ici

obtenir
insérez la description de l'image ici

4. Mise en œuvre du code

/**
 * 归并排序
 * @author 尹稳健~
 * @version 1.0
 * @time 2022/9/12
 */
public class MergeSort {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    1, 5, 6, 3, 2, 8, 7, 4};
        int[] temp = new int[arr.length];
        mergeSort(arr,0,arr.length-1,temp);
        for (int i = 0; i < arr.length; i++) {
    
    
            System.out.print(arr[i]+" ");
        }
    }

    /** 分解,并合并排序 */
    public static void mergeSort(int[] arr,int left ,int right,int[] temp){
    
    
        if (left<right){
    
    
            //中间索引
            int mid = (left+right)/2;
            //向左递归分解
            mergeSort(arr,left,mid,temp);
            //向右递归分解
            mergeSort(arr,mid+1,right,temp);
            //合并
            merge(arr,left,mid,right,temp);
        }
    }

    /**
     * 合并排序
     * @param arr   未排序的数组
     * @param left  左边有序子数组的初始索引
     * @param mid   中间索引
     * @param right 右边最大索引
     * @param temp  临时数组
     */
    public static void merge(int[] arr,int left,int mid,int right,int[] temp){
    
    
        // 左边指针索引
        int leftPoint = left;
        // 右边指针索引
        int rightPoint = mid+1;
        // 临时数据的指针
        int tempLeft = 0;

        // 两个序列都不为空时
        while (leftPoint <= mid && rightPoint <= right){
    
    
            //如果第一个序列的元素小于第二序列的元素,就将其放入temp中
            if (arr[leftPoint] <= arr[rightPoint]){
    
    
                temp[tempLeft] = arr[leftPoint];
                leftPoint++;
                tempLeft ++;
            }else{
    
    
                temp[tempLeft] = arr[rightPoint];
                rightPoint++;
                tempLeft ++;
            }
        }

        // 右序列为空,直接将做序列的所有元素填充进去
        while (leftPoint <= mid){
    
    
            temp[tempLeft] = arr[leftPoint];
            leftPoint++;
            tempLeft++;
        }

        // 左序列为空,直接将右序列的所有元素填充进去
        while (rightPoint <= right){
    
    
            temp[tempLeft] = arr[rightPoint];
            rightPoint++;
            tempLeft++;
        }

        //将临时数组中的元素放回数组arr中
        tempLeft = 0;
        leftPoint = left;
        while (leftPoint <= right){
    
    
            arr[leftPoint] = temp[tempLeft];
            leftPoint++;
            tempLeft++;
        }


    }
}



tri par base

1. Introduction

Le tri par base (radix sort) appartient au "tri par distribution", également connu sous le nom de "tri par compartiment" ou tri par bac. Comme son nom l'indique, il distribue les éléments à trier via des informations partielles sur les valeurs clés à certains "compartiments", dans l'ordre pour remplir le rôle de tri, la méthode de tri par base est un tri stable, et sa complexité temporelle est O (nlog®m), où r est la base utilisée, et m est le nombre de tas, À un moment donné, le tri par base est plus efficace que les autres types de stabilité.

2. Analyse de la pensée

  • Unifiez toutes les valeurs à comparer (entiers positifs) dans la même longueur de chiffres, et les nombres complétés par des zéros avec des chiffres plus courts
  • En commençant par le bit le plus bas, triez-les un par un
  • Tri du chiffre le plus bas au chiffre le plus élevé (chiffre des unités -> chiffre des dizaines -> chiffre des centaines -> ... -> chiffre le plus élevé) une fois le tri terminé, la séquence de nombres devient une séquence ordonnée
  • Le nombre de chiffres dont nous avons besoin pour obtenir le nombre maximum
    • Vous pouvez changer le nombre maximum en un type String, puis trouver sa longueur

3. Diagramme

Tout d'abord, il doit y avoir 10 compartiments représentant [0-9]

3.1 Le premier tour est mis dans le seau selon les unités

insérez la description de l'image ici
Ensuite, sortez-les un par un. S'il y a plusieurs éléments dans un seau, celui qui entre en premier sera retiré en premier.
insérez la description de l'image ici

3.2 Le deuxième tour est mis dans le seau selon les dizaines

insérez la description de l'image ici
S'il n'y a pas de chiffre des dizaines, ajouter 0 devant le chiffre des unités

insérez la description de l'image ici

3.3 Le troisième tour est mis dans le baril selon la place des centaines

insérez la description de l'image ici

Après avoir sorti un par un
insérez la description de l'image ici

3.34 Le quatrième tour est mis dans le seau selon des milliers

insérez la description de l'image ici

Sortez-le pour obtenir :

insérez la description de l'image ici

4. Mise en œuvre du code


/**
 * 基数排序
 * @author 尹稳健~
 * @version 1.0
 * @time 2022/9/13
 */
public class BaseSorted {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    43, 52, 1, 89, 190};
        sort(arr);
        for (int i = 0; i < arr.length; i++) {
    
    
            System.out.print(arr[i] + " ");
        }
    }

    public static void sort(int[] arr){
    
    
        int maxLength = getMaxLength(arr);
        // 定义个二维数组桶,存储元素
        int[][] bucket = new int[10][arr.length];
        // 存储每个桶中有多少个元素
        int[] elementCounts = new int[10];
        for (int times = 1,step = 1; times < maxLength + 1; times++,step *= 10) {
    
    
            // 遍历数组,将元素存入桶中
            for (int i = 0; i < arr.length; i++) {
    
    
                // 获取位数上的数
                int digits = arr[i] / step % 10;
                bucket[digits][elementCounts[digits]] = arr[i];
                elementCounts[digits] ++;
            }

            //将桶中的元素重新放回到数组中
            //用于记录应该放入原数组的哪个位置
            int index = 0;
            for (int i = 0; i < 10; i++) {
    
    
                // 从桶中按放入顺序依次取出元素,放入原数组
                int position = 0;
                while ( elementCounts[i] > 0){
    
    
                    arr[index] = bucket[i][position];
                    position++;
                    elementCounts[i]--;
                    index++;
                }
            }



        }

    }

    /** 获取数组中元素长度最长的长度 */
    public static int getMaxLength(int[] arr){
    
    
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
    
    
            if (arr[i] > max ){
    
    
                max = arr[i];
            }
        }
        return (max + "").length();
    }
}

Je suppose que tu aimes

Origine blog.csdn.net/weixin_46073538/article/details/126802732
conseillé
Classement