2017-10-14 2 views
1

Supposons que vous ayez à trier une liste de modifications, qui peut contenir des éléments de doublons. Par exemple vous avez eu [99, 99, 90, 89], mais la valeur du 4ème élément a changé à 91. Vous voudriez que ce soit [99, 99, 91, 90], mais vous ne voudriez pas l'ordre du premier et le deuxième élément à changer.Conservez l'ordre relatif des éléments de même valeur lorsque vous continuez à trier une liste.

J'ai utilisé la méthode Sort() mais il semble que cela puisse changer l'ordre des premier et deuxième éléments de l'exemple ci-dessus. Y at-il un moyen de l'empêcher et de garder l'ordre relatif des éléments qui ont les mêmes valeurs? Dans le cas où vous ne pouvez pas savoir pourquoi cela serait nécessaire, supposons une liste de progression. Les éléments de la liste sont mis à jour toutes les secondes. Lorsque les éléments sont triés en fonction de la progression, vous souhaitez que les éléments de même progression continuent de modifier leurs positions relatives.

J'ai créé un exemple de code pour le tester. L'objectif atteindrait Debug.WriteLine("No change");.

public void Start() 
{ 
    var list = new List<Item>(); 
    var ran = new Random(); 
    const int nItems = 30; 
    for (int i = 0; i < nItems; i++) 
    { 
     var name = "Item " + (list.Count + 1); 
     var item = new Item(name, ran.Next(0, 10)); 
     list.Add(item); 
    } 

    var sorter = new ItemComparer(); 
    var snapshot = new Item[nItems]; 
    for (int nSort = 0; nSort < 10000; nSort++) 
    { 
     list.CopyTo(snapshot); 
     list.Sort(sorter); 

     if (nSort == 0) 
     { 
      //Sorted for the first time, so the snapshot is invalid. 
      continue; 
     } 

     for (int pos = 0; pos < nItems; pos++) 
     { 
      if (snapshot[pos] != list[pos]) 
      { 
       Debug.WriteLine($"Order changed at position {pos} after {nSort} sorts."); 
       PrintChangedLocation(list, snapshot, pos); 
       return; 
      } 
     } 
    } 

    Debug.WriteLine("No change"); 
} 

private static void PrintChangedLocation(List<Item> list, Item[] snapshot, int changedLocation) 
{ 
    Debug.WriteLine($"Before\t\t\tAfter"); 
    for (int pos = 0; pos < list.Count; pos++) 
    { 
     var before = snapshot[pos]; 
     var after = list[pos]; 
     Debug.Write($"{before.Name} = {before.Progress}"); 
     Debug.Write($"\t\t{after.Name} = {after.Progress}"); 

     if (pos == changedLocation) 
     { 
      Debug.Write(" <----"); 
     } 
     Debug.WriteLine(""); 
    } 
} 

class Item 
{ 
    public string Name; 
    public float Progress; 

    public Item(string name, float progress) 
    { 
     Name = name; 
     Progress = progress; 
    } 
} 

class ItemComparer : IComparer<Item> 
{ 
    int Direction = -1; 

    public int Compare(Item x, Item y) 
    { 
     return Direction * (int)(x.Progress - y.Progress); 
    }  
} 

Exemple de sortie:

Order changed at position 12 after 1 sorts. 
Before   After 
Item 7 = 9  Item 7 = 9 
Item 24 = 9  Item 24 = 9 
Item 30 = 8  Item 30 = 8 
Item 4 = 8  Item 4 = 8 
Item 19 = 8  Item 19 = 8 
Item 27 = 7  Item 27 = 7 
Item 5 = 7  Item 5 = 7 
Item 25 = 7  Item 25 = 7 
Item 20 = 7  Item 20 = 7 
Item 26 = 6  Item 26 = 6 
Item 14 = 6  Item 14 = 6 
Item 1 = 6  Item 1 = 6 
Item 28 = 5  Item 2 = 5 <---- 
Item 2 = 5  Item 12 = 5 
Item 12 = 5  Item 28 = 5 
Item 11 = 4  Item 11 = 4 
Item 6 = 4  Item 6 = 4 
Item 13 = 3  Item 13 = 3 
Item 3 = 3  Item 3 = 3 
Item 21 = 3  Item 21 = 3 
Item 10 = 3  Item 10 = 3 
Item 18 = 3  Item 18 = 3 
Item 22 = 2  Item 22 = 2 
Item 29 = 2  Item 29 = 2 
Item 23 = 1  Item 23 = 1 
Item 8 = 1  Item 8 = 1 
Item 17 = 1  Item 17 = 1 
Item 16 = 0  Item 9 = 0 
Item 9 = 0  Item 16 = 0 
Item 15 = 0  Item 15 = 0 
+1

Le terme que vous recherchez est «stable». Certains algorithmes sont et d'autres ne le sont pas. – Crowcoder

+0

Ah, merci pour l'indice. En ce moment, je lis un article sur ce sujet. –

Répondre

0

Une option à envisager serait en train de changer:

list.Sort(sorter); 

à:

list = list 
    .Select((item, index) => new { item, index}) 
    .OrderBy(z => z.item, sorter) 
    .ThenBy(z => z.index) 
    .Select(z => z.item) 
    .ToList(); 

Cela vous permet trier par votre comparateur, puis par sa position dans le List - gardant ainsi l'ordre relatif.

+1

Merci. Je l'ai résolu en utilisant l'insertion sort (stable) au lieu du 'Sort()' intégré qui utilise le tri rapide (unstable), comme Crowcoder l'a suggéré, mais cela semble aussi être une solution. –