Løsningsforslag - oppgaver i Avsnitt 5.3.3


Oppgave 5

Hvis antall er et partall, så er sammenligningen 2*k < antall identisk med k < antall/2. Men ikke hvis antall er et oddetall. Se f.eks. på det høyre treet i Figur 5.3.3 b). Der er antall lik 13. Dermed vil k = 6 slippe gjennom 2*k < antall siden 2*6 er mindre enn 13. Men k = 6 vil ikke slippe gjennom k < antall/2 siden 13/2 er lik 6 (heltallsdivisjon) og 6 er ikke mindre enn 6.

Dette betyr at hvis vi bruker while (k < antall/2) vil løkken kunne stoppe en node for tidlig. Dvs. at noden med posisjon k vil etterpå kunne ha både to og ett barn. Det kan vi imidlertid teste på. Dermed kan metoden minimumsGrenen kodes slik:

  public String minimumsGrenen()
  {
    StringBuilder s = new StringBuilder();
    s.append('[');

    if (antall > 0) s.append(heap[1]);  // treet er ikke tomt

    int k = 1;
    while (k < antall/2)           // så lenge k har to barn
    {
      k *= 2;                      // går til venstre
      if (comp.compare(heap[k + 1], heap[k]) < 0) k++;
      s.append(',').append(' ').append(heap[k]);
    }

    if (2*k <= antall)             // k har barn
    {
      s.append(',').append(' ');   // komma og mellomrom
      k *= 2;

      // finner det minste hvis det er to barn
      if (k < antall && comp.compare(heap[k + 1], heap[k]) < 0) k++;

      s.append(heap[k]);
    }
    s.append(']');

    return s.toString();
  }

Vi kan gjøre de samme endringene i metoden taUt:

  public T taUt()
  {
    if (tom()) throw new NoSuchElementException("Køen er tom!");

    T min = heap[1];                   // den minste ligger øverst
    T verdi = heap[antall];            // skal omplasseres

    heap[antall] = null;               // for resirkulasjon
    antall--;                          // en verdi mindre i køen    

    int k = 1;                         // nodeposisjon
    int halvparten = antall/2;         // istedenfor antall/2

    while (k < halvparten)             // så lenge k har to barn
    {
      k <<= 1;                         // til venstre ved å doble k

     // hvis høyre barn k + 1 er minst, setter vi k dit, dvs. k++
     if (comp.compare(heap[k + 1], heap[k]) < 0) k++;

      heap[k >>> 1] = heap[k];         // forskyver oppover
    }

    if (2*k <= antall)                 // k har barn
    {
      k *= 2;                          // går til venstre barn

      if (k < antall && comp.compare(heap[k + 1], heap[k]) < 0) k++;

      heap[k/2] = heap[k];               // forskyver oppover
    }

    heap[0] = verdi;                   // blir vaktpost

    while (comp.compare(heap[k/2],verdi) > 0)
    {
      heap[k] = heap[k/2];             // trekker verdien nedover
      k /= 2;                          // k går opp til forelderen
    }
    heap[k] = verdi;                   // verdi skal ligge i posisjon k    

    return min;                        // returnerer minste verdi
  }

Oppgave 6

If-setningen etter første while-løkke i taUt-metoden kan fjernes hvis while-løkken kodes slik som nedenfor. Ulempen er at at vi får en ekstra sammenligning, dvs. k < antall i hver eneste runde av while-løkken. Men k < antall er enten alltid sann eller eventuelt usann siste gang (nederst i grenen). Med andre ord blir koden litt mindre effektiv.

  while ((k << 1) <= antall)          // så lenge k har minst ett barn
  {
    k <<= 1;                         // til venstre ved å doble k
    if (k < antall && comp.compare(heap[k + 1], heap[k]) < 0) k++;
    heap[k >>> 1] = heap[k];         // forskyver oppover
  }

Hvis vi i tillegg bruker teknikken fra Oppgave 5, kan koden effektiviseres noe. Da blir den slik:

  int k = 1;                         // nodeposisjon
  int halvparten = antall/2;         // istedenfor antall/2

  while (k <= halvparten)            // så lenge k har minst ett barn
  {
    k <<= 1;                         // til venstre ved å doble k
    if (k < antall && comp.compare(heap[k + 1], heap[k]) < 0) k++;
    heap[k >>> 1] = heap[k];         // forskyver oppover
  }

Oppgave 7

Oppgave 8

  public String[] grener()
  {
    Liste<String> liste = new TabellListe<>();
    StringBuilder s = new StringBuilder("[");
    if (!tom()) grener(1, liste, s);

    String[] grener =
      new String[liste.antall()];           // oppretter tabell

    int i = 0;
    for (String gren : liste)
      grener[i++] = gren;                   // fra liste til tabell

    return grener;                          // returnerer tabellen
  }

  private void grener(int k, Liste<String> liste, StringBuilder s)
  {
    T verdi = heap[k];
    int n = verdi.toString().length(); // lengden på verdi

    if (2*k > antall)  // bladnode
    {
      liste.leggInn(s.append(verdi).append(']').toString());

      // må fjerne det som ble lagt inn sist - dvs. n + 1 tegn
      s.delete(s.length() - n - 1, s.length());
    }
    else
    {
      s.append(heap[k]).append(',').append(' ');  // legger inn n + 2 tegn
      if (2*k <= antall) grener(2*k, liste, s);
      if (2*k < antall) grener(2*k + 1, liste, s);
      s.delete(s.length() - n - 2, s.length());   // fjerner n + 2 tegn
    }
  }

Oppgave 9

  public boolean taUt(T verdi)         // skal legges i klassen HeapPrioritetsKø
  {
    if (verdi == null) return false;   // køen har ikke nullverdier

    T omplasseres = heap[antall];      // skal omplasseres
    heap[antall] = null;               // for resirkulasjon
    antall--;                          // en verdi mindre i køen  

    // sjekker om det er siste verdi som skal tas ut
    if (comp.compare(verdi,omplasseres) == 0) return true;

    int k = 1; for (; k <= antall; k++)  // leter etter verdien som skal ut
    {
      if (comp.compare(verdi,heap[k]) == 0) break;
    }

    if (k > antall) return false;      // fant ikke verdi

    // k er nå posisjonen til verdien som skal tas ut
    while ((k << 1) < antall)          // så lenge k har to barn
    {
      k <<= 1;                         // til venstre ved å doble k

      // hvis høyre barn k + 1 er minst, setter vi k dit, dvs. k++
      if (comp.compare(heap[k + 1], heap[k]) < 0) k++;

      heap[k >>> 1] = heap[k];         // forskyver oppover
    }

    if (2*k == antall)                 // har k et barn?
    {
      k *= 2;                          // går til venstre barn
      heap[k/2] = heap[k];             // forskyver oppover
    }

    // verdien som skal omplasseres settes inn på rett sortert
    // plass i grenen som ender i noden med posisjon k

    heap[0] = omplasseres;             // blir vaktpost

    while (comp.compare(heap[k/2],omplasseres) > 0)
    {
      heap[k] = heap[k/2];             // trekker verdien nedover
      k /= 2;                          // k går opp til forelderen
    }

    heap[k] = omplasseres;             // verdi skal ligge i posisjon k      

    return true;
  }