Strona główna » Algorytmy » Sortowanie » Minimalna Liczba Zamian
 

Minimalna Liczba Zamian

Cel

Różne operacje wykonywane na komputerze mogą mieć różny koszt. W przypadku, gdy koszt zapisu jest bardzo duży, a trzeba posortować tablicę to warto wykonać tylko tyle operacji ile trzeba. W tym artykule zostanie wyjaśnione jak znaleźć ile potrzeba zamian, aby posortować tablicę liczb.

Zadanie

Napisany algorytm ma zwrócić liczbę potrzebnych zamian, aby dana tablica liczb całkowitych, dodatnich została posortowana. Liczby w tablicy się nie powtrzają. Zamieniać można dwa elementy leżące w dowolnych miejscach.

Analiza Zadania

Weźmy jako przykład tablicę [3, 2, 1]. Do posortowania tej tablicy rosnąco potrzeba zaledwie jednej zamiany tj. elementu 1 i 3. Jeśli podany przypadek rozrysuje się jako graf można zauważyć, że strzałki tworzą cykl o długości 2.

Porządkowanie tablicy [3, 2, 1]

Spróbujmy podobnie rozpisać dla tablicy [2, 3, 1, 5, 4]. W tym przypadku tworzą się dwa cykle, które można posortować oddzielnie.

Porządkowanie tablicy [2, 3, 1, 5, 4]

Ile tym razem potrzeba zamian? Dokładnie 3. Wynika to z faktu, że lewą stronę można posortować w dwóch ruchach, a prawą w jednym. Zauważmy, że istnieje tutaj zależność pomiędzy długością cyklu, a ilością zamian: w celu posortowania cyklu długości n potrzeba n - 1 zamian. Oznacza to, że trzeba znaleźć wszystkie długości cykli i pomniejszyć o ilość cykli.

Implementacja

Poniższa funkcja IleZamian() zwraca dla podanej tablicy liczb ile minimalnie potrzeba zmian.

  1. static int IleZamian(List<int> tablica)
  2. {
  3.   List<Tuple<int, int>> dane = new List<Tuple<int, int>>();
  4.   for (int i = 0; i < tablica.Count; i++)
  5.   {
  6.     dane.Add(new Tuple<int, int>(tablica[i], i));
  7.   }
  8.   dane.Sort();
  9.   int zamian = 0;
  10.   for (int i = 0; i < tablica.Count; i++)
  11.   {
  12.     if (dane[i].Item1 == -1)
  13.       continue;
  14.     int dl = 1;
  15.     int j = i;
  16.     while (dane[j].Item1 != -1)
  17.     {
  18.       dl++;
  19.       j = dane[j].Item2;
  20.       dane[j] = new Tuple<int, int>(-1, j);
  21.     }
  22.     if (dl == 0)
  23.       continue;
  24.     zamian += dl - 1;
  25.   }
  26.   return zamian;
  27. }

Na początku tworzymy pary (liczba × pozycja), a następnie sortujemy. Teraz znamy wszystkie krawędzie, który wystepują w grafie, ponieważ jeśli nowy pozycja jest różna od tej zapamiętanej to znaczy, że dana wartość musimy przesunąć. Jeśli wartości są takie same to znaczy, że nie istnieje potrzeba zamiany. Dla liczb, który trzeba zamienić uruchamiamy pętlę, która od pozycji i przechodzimy na pozycję z której dana liczba pochodzi. Kontynuując ten proces natrafimy z powrotem na pozycję z której wyszliśmy. Po drodze należy zliczyć ile takich przeskoków trzeba było wykonać, ponieważ jest to długość cyklu, którą pomniejszoną o 1 doliczamy do liczby zamian. Należy też pamiętać, aby oznaczać miejsca już odwiedzone, aby danego cyklu nie policzyć kilka razy. W tym algorytmie odwiedzona wartość zostaje zamieniona na -1.

Testowanie funkcji

W celu przetestowania kodu można skorzystać z poniższego kodu, który wczyta potrzebne dane i wypisze wynik:

  1. static void Main(string[] args)
  2. {
  3.   Console.WriteLine("Podaj liczby:");
  4.   string[] dane_surowe = Console.ReadLine().Split();
  5.   List<int> dane = new List<int>();
  6.   foreach (string el in dane_surowe)
  7.     dane.Add(Convert.ToInt32(el));
  8.   int wynik = IleZamian(dane);
  9.   Console.WriteLine("Potrzebnych zmian: {0}", wynik);
  10.   Console.ReadKey();
  11. }