Algoritmer og datastrukturer
Løsningsforslag

Eksamen  −  23. februar 2011

Oppgave 1A

Et Huffmantre blir alltid et fullt tre, dvs. alle indre noder har to barn. Hvis vi tegner et tre ved hjelp av de bitkodene som er oppgitt, vil en se at både B-noden og N-noden mangler søsken. Søskenen til B-noden vil få 4 som avstand til roten, mens søskenen til N-noden vil få 3 som avstand. Dermed må K bli søsken til N og M søsken til B:

Dekomprimeringen gir flg. resultat: EKSAMEN



Oppgave 1B

Verdiene 2, 5, 3, 1 og 4 legges fortløpende på stakken. Hver ny innlegging havner på toppen av stakken. Det betyr at stakkens innhold fra toppen og nedover til bunnen blir 4, 1, 3, 5 og 2. Ved et uttak er det alltid det på toppen som tas ut. Når verdiene fortløpende tas fra stakken og skrives ut, blir utskriften derfor 4 1 3 5 2. Med andre ord får vi verdiene i motsatt rekkefølge av hva den oprinnelig var.

Oppgave 1C


Oppgave 1D

  public static <T> T maks(Iterable<T> s, Comparator<? super T> c)
  {
    Iterator<T> i = s.iterator();
    if (!i.hasNext()) throw new NoSuchElementException("s er tom!");

    T maks = i.next();

    while (i.hasNext())
    {
      T verdi = i.next();
      if (c.compare(verdi,maks) > 0) maks = verdi;
    }
    return maks;
  }

Oppgave 2A

  public static <T> void forskyv(T[] a)
  {
    if (a.length < 2) return;

    T temp = a[a.length - 1];  // tar vare på den bakerste

    for (int i = a.length - 1; i > 0; i--) a[i] = a[i-1];

    a[0] = temp;  // legger inn forrest
  }

Oppgave 2B

Hvis k er større enn lengden til tabellen a, kan vi gjøre forenklinger. Eksempeltabellen i oppgaveteksten har lengde 10. La f.eks. k = 25. Men det blir det samme som å forskyve 5 ganger siden hver forskyvning på 10 enheter gjør at tabellen blir slik den var. Det blir tilsvarende med en negativ k. Hver forskyvning på -10, dvs. 10 enheter mot venstre, gjør at tabellen blir slik den var. Med andre ord kan vi erstatte k med k % a.length. Videre vil en forskyvning på f.eks. 3 mot venstre (k = −3), være det samme som en forskyvning på -3 + 10 = 7 mot høyre:

  public static void bytt(Object[] a, int v, int h)
  {
    Object temp = a[v]; a[v] = a[h]; a[h] = temp;
  }

  public static <T> void forskyv(T[] a, int k)
  {
    if (a.length < 2) return;

    k = k % a.length;

    if (k < 0) k += a.length;

    int v = 0, h = a.length - 1;
    while (v < h) bytt(a,v++,h--);  // snur hele tabellen

    v = 0; h = k - 1;
    while (v < h) bytt(a,v++,h--);  // snur de k første

    v = k; h = a.length - 1;
    while (v < h) bytt(a,v++,h--);  // snur resten
  }

Oppgave 3A

Det er en gren for hver bladnode:

Inorden: 10, 7, 12, 4, 9, 5, 3, 5, 3, 9, 2, 11, 7, 6, 8

Oppgave 3B

  public T kikk()
  {
    if (tom()) throw new NoSuchElementException("Treet er tomt!");
    return rot.verdi;
  }

  public int antall()
  {
    if (tom()) return 0;
    return rot.antall;
  }

  public boolean tom()
  {
    return rot == null;
  }

Oppgave 3C

Her må vi traversere treet og for hver node sjekke at antallet noder i venstre subtre er minst så stort som antallet i høyre subtre. Antallet i et tomt tre er 0. For å forenkle kodingen kan det være lurt å lage en hjelpemetode som finner antallet i et tre også i det tilfellet at det er tomt. Velger her en traversering i nivåorden:

  private static <T> int antall(Node<T> p)
  {
    if (p == null) return 0; else return p.antall;
  }

  public boolean erVenstretungt()
  {
    if (rot == null) return true;
    Kø<Node<T>> kø = new TabellKø<>();
    kø.leggInn(rot);

    while (!kø.tom())
    {
      Node<T> p = kø.taUt();

      if (antall(p.venstre) < antall(p.høyre))
        return false;  // ikke venstretungt

      if (p.venstre != null) kø.leggInn(p.venstre);
      if (p.høyre != null) kø.leggInn(p.høyre);
    }
    return true;
  }

Oppgave 3D

Verdien 10 skal først inn på rett sortert plass i treets høyre kant, dvs. som høyre barn til 8-noden. Men da vil ikke lenger 8-noden ha venstretyngde. Det repareres ved at nodens to subtrær bytter plass. Dermed blir 10-noden isteden venstre barn til 8-noden. De andre nodene på høyre kant (2-noden og 6-noden) vil da fortsatt ha venstretyngde:

Verdien 12 settes inn på rett sortert plass i treets høyre kant når den settes inn som høyre barn til 8-noden. Da vil 8-noden fortsatt ha venstretyngde. Men det har ikke lenger 6-noden. Der må vi etterpå bytte om dens subtrær. Rotnoden blir ikke påvirket. Den vil fortsatt ha venstretyngde:

Verdien 5 settes inn på rett sortert plass i treets høyre kant når den settes inn mellom 2-noden og 6-noden. Den nye noden får da 6-noden som høyre barn. Men da vil ikke den nye noden få venstretyngde. Det løses ved at nodens to subtrær bytter plass:

Bytting av subtrær kan gjøres på vei nedover til rett plass fordi vi vet allerede da hvilke noder som ikke lenger vil ha venstretyngde. F.eks. hvis ny verdi er mindre enn eller lik verdien i rotnoden, vil den nye verdien bli ny rot og den gamle roten blir dens venstre barn. Osv.

  public void leggInn(T verdi)
  {
    if (rot == null)
      rot = new Node<>(verdi,1);
    else if (comp.compare(verdi,rot.verdi) <= 0)
      rot = new Node<>(verdi,rot.antall + 1,rot,null);
    else
    {
      Node<T> q = rot, p = rot.høyre;

      while (p != null && comp.compare(verdi, p.verdi) > 0)
      {
        q.antall++;
        if (q.venstre.antall == q.høyre.antall) byttSubtrær(q);
        q = p;
        p = p.høyre;
      }
      q.antall++;

      if (p == null)
      {
        if (q.venstre == null) q.venstre = new Node<>(verdi,1);
        else q.høyre = new Node<>(verdi,1);
      }
      else
      {
        q.høyre = new Node<>(verdi,p.antall + 1,p,null);
        if (q.venstre.antall < q.høyre.antall) byttSubtrær(q);
      }
    }
  }