Java kiegészítő lecke – Többszörös rendezés

Csak akkor olvass tovább, ha a többszörös rendezés feladatot megoldottad, vagy nem sikerült megoldani.

Adott tehát egy Kutya osztály. Minden kutyáról 2 tulajdonságot tartunk nyilván. Ezt kellene előbb fajta, majd azonos fajta esetén életkor szerint rendezni.

A megoldás

A feladat megoldásakor kétszer rendezünk. Először az első tulajdonság szerint egy tetszőleges rendezési algoritmussal:

Kutya[] kutyak =
  {
    new Kutya("tacsko", 3),
    new Kutya("labrador", 7),
    new Kutya("labrador", 5),
    new Kutya("vizsla", 1),
    new Kutya("labrador", 2),
    new Kutya("vizsla", 3),
    new Kutya("labrador", 1),
    new Kutya("tacsko", 2)
  };

// első rendezés az első tulajdonság szerint (fajta)
Kutya csere;
for( int i = 0; i < kutyak.length-1; i++ )
{
  for( int j = i+1; j < kutyak.length; j++ )
  {
    if( kutyak[i].getFajta().compareTo( kutyak[j].getFajta() ) > 0 )
    {
      csere = kutyak[i];
      kutyak[i] = kutyak[j];
      kutyak[j] = csere;
    }
  }
}

Ha ez megvan, akkor úgy rendezünk a második tulajdonság alapján, hogy az első tulajdonságokat már nem keverjük össze. A kiemelt sorban látható az a feltétel, mely biztosítja, hogy csak akkor vizsgáljuk meg a második tulajdonságot, ha az első tulajdonságuk megegyezik! Ha ez teljesül ÉS az életkor nem jó sorrendben áll, akkor cserélünk. Nagyon fontos a két feltétel vizsgálatának sorrendje!

// második rendezés a második tulajdonság esetén
for( int i = 0; i < kutyak.length-1; i++ )
{
  for( int j = i+1; j < kutyak.length; j++ )
  {
    if( kutyak[i].getFajta().equals( kutyak[j].getFajta() ) &&
        kutyak[i].getKor() > kutyak[j].getKor() )
    {
      csere = kutyak[i];
      kutyak[i] = kutyak[j];
      kutyak[j] = csere;
    }
  }
}
// kiíratás ellenőrzésképp, a sorrend valóban megfelelő
for( Kutya k : kutyak )
{
  System.out.println( k.toString() );
}

A megoldás két ciklust igényel, ami azért lássuk be, nem túl hatékony. Lehet-e még optimalizálni, egyszerűsíteni rajta?

Egyszerűbb megoldás

Lássunk egy olyan megoldást, amely egyetlen ciklussal oldja meg a két oszlop szerinti rendezést:

Kutya[] kutyak =
  {
    new Kutya("tacsko", 3),
    new Kutya("labrador", 7),
    new Kutya("labrador", 5),
    new Kutya("vizsla", 1),
    new Kutya("labrador", 2),
    new Kutya("vizsla", 3),
    new Kutya("labrador", 1),
    new Kutya("tacsko", 2)
  };

Kutya csere;
for( int i = 0; i < kutyak.length-1; i++ )
{
  for( int j = i+1; j < kutyak.length; j++ )
  {
// ha a fajtájuk szerint az elől lévő a nagyobb (abc rend)
    if( kutyak[i].getFajta().compareTo( kutyak[j].getFajta() ) > 0 ||
// ha a fajtájuk egyenlő ÉS az elől lévő életkora nagyobb
        ( kutyak[i].getFajta().equals( kutyak[j].getFajta() ) &&
          kutyak[i].getKor() > kutyak[j].getKor() ) )
    {
      csere = kutyak[i];
      kutyak[i] = kutyak[j];
      kutyak[j] = csere;
    }
  }
}
for( Kutya k : kutyak )
{
  System.out.println( k.toString() );
}

Akkor tételesen a magyarázat a kiemelt sorokra vonatkozóan:

  • 12 – Ha a két kutya fajtáját összehasonlítjuk, és az elől lévő a nagyobb, akkor meg kell cserélni a kettőt. Mivel a következő feltétel VAGY művelettel kapcsolódik, ezért ekkor a többi feltételt nem is vizsgáljuk, ha az első igaz
  • 14 – Ha az előző feltétel hamis, vagyis az elől lévő nincs hátrébb az abc rendben, akkor megnézzük, hogy ugyanabba a fajtába tartoznak-e. Ha igen, csak akkor vizsgáljuk meg az életkorukat, így nem keverjük össze a fajtákat.
  • 15 – ÉS ha egyező fajta esetén az elől lévő az idősebb, akkor megcseréljük őket

Ha jobban megnézed, gyakorlatilag a két rendezéses feladat feltételeit kellett VAGY-gyal összekötni, és egy ciklussal megoldható a feladat.

Tisztább megoldás

Most hogy már ezt a feladatot is meg tudod oldani, még egy picit terelgetnélek a jobb programozási technikák felé. A helyzet az, hogy az ilyen rendezési feltételt, ami esetleg többször is felhasználásra kerülhet, vagy nagyon átláthatatlanná teszi a kódot, érdemesebb kiszervezni egy metódusba. Ennek az az oka, hogy később akár magába a Kutya osztályba is berakhatjuk, aminek a beépített rendezések nagyon fognak örülni!

A kód gyakorlatilag így módosul, lássuk egyben az egészet:

public static void main( String[] args )
{
  Kutya[] kutyak =
  {
    new Kutya("tacsko", 3),
    new Kutya("labrador", 7),
    new Kutya("labrador", 5),
    new Kutya("vizsla", 1),
    new Kutya("labrador", 2),
    new Kutya("vizsla", 3),
    new Kutya("labrador", 1),
    new Kutya("tacsko", 2)
  };

  System.out.println();

  Kutya csere;
  for ( int i = 0; i < kutyak.length - 1; i++ )
  {
    for ( int j = i + 1; j < kutyak.length; j++ )
    {
      if ( rendezesFajtaEletkor( kutyak[i],kutyak[j] ) )
      {
        csere = kutyak[i];
        kutyak[i] = kutyak[j];
        kutyak[j] = csere;
      }
    }
  }
  for ( Kutya k : kutyak )
  {
    System.out.println(k.toString());
  }
}

public static boolean rendezesFajtaEletkor( Kutya a, Kutya b )
{
  if ( a.getFajta().compareTo( b.getFajta() ) > 0 ||
       ( a.getFajta().equals( b.getFajta() ) &&
         a.getKor() > b.getKor() ) )
  {
    return true;
  }
  else
  {
    return false;
  }
}

A lényeg, hogy a rendezés összetett feltételét kirakjuk egy olyan nevű metódusba, melynek neve természetesen utal arra, hogy az mit fog csinálni. Ez a metódus csak egy választ ad arra, hogy a feltételnek megfelelően ki kell-e cserélni a két kutyát, vagy nem. Később ezt a metódust átrakhatjuk közvetlenül a Kutya osztályba, ha ez lesz a kutyák általános sorba rendezési módja.

A metódus még mindig egyszerűsíthető::

public static boolean rendezesFajtaEletkor( Kutya a, Kutya b )
{
  return a.getFajta().compareTo( b.getFajta() ) > 0 ||
         ( a.getFajta().equals( b.getFajta() ) &&
           a.getKor() > b.getKor() );
}

Nincs feltételvizsgálat, hiszen az összetett kifejezés önmagában már a választ jelenti, hogy kell-e cserélni, vagy sem.

Két tulajdonság szerinti többszintű rendezésnél bonyolultabb nem hinném, hogy lenne a rendezés témakörből középiskolai szinten. Ott is úgy gondolom, hogy inkább bizonyos feladatok megoldását könnyíti meg. Ha az egy ciklusos megoldásig eljutsz úgy, hogy alkalmazni is tudod, akkor már nem volt hiába ez a feladat. De a végső cél az, hogy az ilyen rendezési módokat át helyezni metódusba, ami majd az objektumok összehasonlítását könnyíti meg a beépített rendezéseknek.

Nincs határ

Nincs korlátja annak, hány oszlop szerint lehet rendezni, ennek csak és kizárólag a tudásod szabhat határt. Ha a kutyáknak lenne egy szín tulajdonsága is, akkor olyan rendezés is írható, melyben először fajta, majd életkor, végül szín szerint rendezi őket. Csak a feltétel lesz bonyolultabb, a megoldás azon múlik, össze tudod-e rakni. Akkor már három ciklust spórolhatsz meg azzal, hogy megértetted a feltételek összevonását. Nyilván a metódus nevében is szerepeltetni kell, hogy mit csinál, ahogy eddig is tetted. De csak hogy bizonyítsam, hogy tényleg lehetséges, itt a metódus hozzá, kutyákat is adok, de az osztályt gyártsd le magadnak hozzá. A metódusban pár bónusz üres sorral azért még segítek, hogy megérthesd. Jó bogarászást!

Kutyák:

Kutya[] kutyak =
{
  new Kutya("tacsko", 3, "fekete"),
  new Kutya("vizsla", 6, "arany"),
  new Kutya("vizsla", 3, "fekete"),
  new Kutya("labrador", 7, "zsemle"),
  new Kutya("labrador", 5, "arany"),
  new Kutya("vizsla", 3, "arany"),
  new Kutya("labrador", 7, "fekete"),
  new Kutya("vizsla", 3, "barna"),
  new Kutya("labrador", 7, "barna"),
  new Kutya("labrador", 1, "zsemle"),
  new Kutya("tacsko", 2, "fekete")
};

Metódus:

public static boolean rendezesFajtaEletkorSzin( Kutya a, Kutya b )
{
  return  a.getFajta().compareTo( b.getFajta() ) > 0 ||

          ( a.getFajta().equals( b.getFajta() ) &&
            a.getKor() > b.getKor() ) ||

          ( a.getFajta().equals( b.getFajta() ) &&
            a.getKor() == b.getKor() &&
            a.getSzin().compareTo( b.getSzin() ) > 0 );
}

Vélemény, hozzászólás?

Az e-mail címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük

*

Ez az oldal az Akismet szolgáltatást használja a spam csökkentésére. Ismerje meg a hozzászólás adatainak feldolgozását .