Je sais que le pire des cas sur mergesort est O (nlogn), le même que le cas moyen.
Cependant, si les données sont ascendantes ou descendantes, cela se traduit par le nombre minimum de comparaisons , et par conséquent le fusionnement devient plus rapide que les données aléatoires. Ma question est donc la suivante: quel type de données d'entrée produit le nombre maximal de comparaisons qui aboutissent à une fusion plus lente?
La réponse à this question dit:
Pour certains algorithmes de tri (par exemple, tri rapide), l'ordre initial des éléments peut affecter le nombre d'opérations à effectuer. Cependant, il ne fait aucun changement pour mergesort car il devra de toute façon faire exactement le même nombre d'opérations: diviser récursivement en petits tableaux puis les fusionner à nouveau, en un temps total de Θ (nlogn).
Mais c'est faux. Au point où nous avons deux sous-réseaux et nous voulons les fusionner si les données initiales sont triées, nous n'aurons que n/2 comparaisons. Il s'agit de tous les éléments du premier sous-tableau avec uniquement le premier élément du deuxième tableau. Cependant, nous pouvons faire plus que cela. Je cherche ces données d'entrée.
Le pire cas de tri par fusion sera celui où le tri par fusion devra faire nombre maximal de comparaisons.
Je vais donc essayer de construire le pire des cas de manière ascendante:
Supposons que le tableau à l'étape finale après le tri soit {0,1,2,3,4,5,6,7}
Dans le pire des cas, le tableau avant cette étape doit être {0,2,4,6,1,3,5,7}
car ici sous-tableau gauche = {0,2,4,6}
et sous-tableau droit = {1,3,5,7}
entraînera des comparaisons maximales. ( Stockage des elemets alternatifs dans les sous-réseaux gauche et droit )
Raison: Chaque élément du tableau sera comparé au moins une fois.
Application de la même logique ci-dessus pour les sous-tableaux gauche et droit pour les étapes précédentes: Pour le tableau {0,2,4,6}
le pire des cas sera si le tableau précédent est {0,4}
et {2,6}
et pour le tableau {1,3,5,7}
le pire des cas sera pour {1,5}
et {3,7}
.
{0,4}
doit être {4,0}
, {2,6}
doit être {6,2}
, {1,5}
doit être {5,1}
{3,7}
doit être {7,3}
. Eh bien, si vous regardez clairement, cette étape est pas nécessaire parce que si la taille de set/array est 2, chaque élément sera comparé au moins une fois même si le tableau de taille 2 est trié.Applying Merge Sort using Divide and Conquer
Input array arr[] = [4,0,6,2,5,1,7,3]
/ \
/ \
[4,0,6,2] and [5,1,7,3]
/ \ / \
/ \ / \
[4,0] [6,2] [5,1] [7,3] Every pair of 2 will be compared atleast once therefore maximum comparison here
| | | |
| | | |
[0,4] [2,6] [1,5] [3,7] Maximum Comparison:Every pair of set is used in comparison
\ / \ /
\ / \ /
[0,2,4,6] [1,3,5,7] Maximum comparison again: Every pair of set compared
\ /
\ /
[0,1,2,3,4,5,6,7]
Maintenant, vous pouvez appliquer la même logique pour n'importe quel tableau de taille n
Voici le programme qui implémente la logique ci-dessus.
Remarque: Le programme ci-dessous n'est pas valable uniquement pour des puissances de 2. C'est une méthode généralisée pour fournir le pire des cas pour tout tableau de taille n. Vous pouvez essayer différents tableaux pour la saisie par vous-même.
class MergeWorstCase
{
public static void print(int arr[])
{
System.out.println();
for(int i=0;i<arr.length;i++)
System.out.print(arr[i]+" ");
System.out.println();
}
public static void merge(int[] arr, int[] left, int[] right) {
int i,j;
for(i=0;i<left.length;i++)
arr[i]=left[i];
for(j=0;j<right.length;j++,i++)
arr[i]=right[j];
}
//Pass a sorted array here
public static void seperate(int[] arr) {
if(arr.length<=1)
return;
if(arr.length==2)
{
int swap=arr[0];
arr[0]=arr[1];
arr[1]=swap;
return;
}
int i,j;
int m = (arr.length + 1) / 2;
int left[] = new int[m];
int right[] = new int[arr.length-m];
for(i=0,j=0;i<arr.length;i=i+2,j++) //Storing alternate elements in left subarray
left[j]=arr[i];
for(i=1,j=0;i<arr.length;i=i+2,j++) //Storing alternate elements in right subarray
right[j]=arr[i];
seperate(left);
seperate(right);
merge(arr, left, right);
}
public static void main(String args[])
{
int arr1[]={0,1,2,3,4,5,6,7};
seperate(arr1);
System.out.print("For array 1:");
print(arr1);
int arr2[]={0,1,2,3,4,5,6,7,8};
seperate(arr2);
System.out.print("For array 2:");
print(arr2);
}
}
Sortie:
For array 1:
4 0 6 2 5 1 7 3
For array 2:
8 0 4 6 2 5 1 7 3
Un algorithme soigné qu'un de mes professeurs m'a donné résout cela en utilisant une approche opposée. Plutôt que de diviser le tableau initial en blocs de plus en plus petits, vous pouvez commencer avec un cas de base et suivre un modèle récursif.
Le cas de base est [1] et [2, 1], qui sont les exemples des pires tableaux de taille 1
et 2
. À partir de là, vous créez des tableaux pour 3
et 4
comme suit.
n
et m
, tels que n + m = x
, où x est la taille que vous recherchezEn utilisant cet algorithme, voici la série d'étapes pour les tableaux de taille 3
et 4
.
3
[1] + [2, 1]
[1 | 2, 1]
[2 | 2, 1]
[2 | 3, 1] -> [2, 3, 1]
4
[2, 1] + [2, 1]
[2, 1 | 2, 1]
[4, 2 | 2, 1]
[4, 2 | 3, 1] -> [4, 2, 3, 1]
7
[2, 3, 1] + [4, 2, 3, 1]
[2, 3, 1 | 4, 2, 3, 1]
[4, 6, 2 | 4, 2, 3, 1]
[4, 6, 2 | 7, 3, 5, 1] -> [4, 6, 2, 7, 3, 5, 1]
Il est facile de voir comment vous pouvez adopter cette approche et créer facilement des tailles de tableau énormes.
Voici une fonction python qui implémente cet algorithme.
import math
def worstCaseArrayOfSize(n):
if n == 1:
return [1]
else:
top = worstCaseArrayOfSize(int(math.floor(float(n) / 2)))
bottom = worstCaseArrayOfSize(int(math.ceil(float(n) / 2)))
return map(lambda x: x * 2, top) + map(lambda x: x * 2 - 1, bottom)