OOP Implementierung eines Heaps

Butterbrot

Aktives Mitglied
Guten Tag Community,

ich habe eine Hausaufgabe von der Uni, die ich bearbeiten muss und habe Verständnisprobleme, wie ich an die Sache rangehen soll. Ich lade mal die Datei hoch, damit ihr euch einmal die Aufgabenstellung durchlesen könnt. Meine Frage bezieht sich nicht auf die einzelnen Methoden, sondern wie ich das grundsätzliche Konstrukt schreiben soll. Hier mein vorläufiger Code:
Java:
public class PriorityQueue<E, EntryType> implements PQInterface<E> {

    private class Entry{

        public int prio;
        public EntryType value;

        public Entry(EntryType value){
            prio = 0;
            this.value = value;
        }
    }

    private ArrayList<Entry> entryList;

    public PriorityQueue(){
        entryList = new ArrayList<>();
    }

    private int getLeftChildIndex(int index){
        return 2*index;
    }

    private int getRightChildIndex(int index){
        return 2*index+1;
    }

    private int getParentIndex(int index){
        return (index/2);
    }

    private void bubbleUp(int index){
//        int parIndex = getParentIndex(index);
        //        int pos = index -1;
        //        while((pos > 0) && (pos/2 > pos)){
        //            pos /=2;
        //        }
//        while (index > 0 && entryList.get(parIndex).prio > entryList.get(index).prio) {
//            swap(parIndex, index);
//            index = parIndex;
//            parIndex = getParentIndex(index);
//        }
//    
//        entryList.set(0, entryList.get(index));  // Acts as sentinal so we don't fall off the array
//
//        int parent = index / 2;
//
//        while (entryList.get(parent) entryList.get(index)) {
//
//            Entry temp = entryList.get(parent);
//
//            entryList.set(parent, entryList.get(index));
//
//            entryList.set(index, temp);
//
//            index = parent;
//
//            parent = index / 2;
//
//        }

        {
             int parent = (index-1) / 2;
             E bottom = entryList.get(index);

             while( index > 0 &&
                   !entryList.get(parent).equals(bottom)  )
                {
               bottom = entryList.get(parent);  // move it down
                index = parent;
                parent = (parent-1) / 2;
                }  // end while
             bottom = entryList.get(index) ;
             }  // end trickleUp()
    }


//    private void swap(int i, int j) {
//        PriorityQueue<E, EntryType>.Entry tmp = entryList.get(i);
//        entryList.set(i, entryList.get(j));
//        entryList.set(j, tmp);
//    }

    private void trickleDown(int index){
        int smallest = index;

        if(getRightChildIndex(index) < index){
            smallest = (getRightChildIndex(index));
        }

        if(getLeftChildIndex(index) < index){
            smallest = getLeftChildIndex(index);
        }

        while (smallest != index){
            trickleDown(smallest);
        }
    }

    @Override
    public int size() {
        return entryList.size();
    }

    @Override
    public boolean isEmpty() {
        return entryList.isEmpty();

    }

    @Override
    public void add(E element, int priority) {
                if(isEmpty()) entryList.add(priority, element);
                else{
                    entryList.add(priority, element);
                    bubbleUp(size());
                }
//
//        entryList.add((PriorityQueue<E, EntryType>.Entry) element);
//        bubbleUp(entryList.size() - 1);

    }

    @Override
    public E peek() throws NoSuchElementException {
        if(isEmpty()) throw new NoSuchElementException();
        else{
            return entryList.get(size());
        }

    }

    @Override
    public E remove() {

//        int result = (int) peek();
        entryList.set(0, entryList.get(entryList.size() - 1));
//        entryList.remove(entryList.size() - 1);
        trickleDown(0);

    

        return null;
    }




public static void main(String args[]){
    ArrayList<Integer> a = new ArrayList<Integer>();
    a.add(4);
    a.add(6);
    a.add(2);
    a.add(5);
    a.add(7);
    a.add(9);
//    System.out.println(a.size());
//    System.out.println(a.isEmpty());
//    System.out.println(a.get(size()).value);
    System.out.println(a.peek());
    System.out.println(a.toString());
//    System.out.println(a.remove(5));
//    System.out.println(a.toString());
}
}

Ich weiß das ist alles noch sehr wirr und in der Entwicklungsphase, aber ich verstehe die genaue Herangehensweise nicht. Mir geht es vorsätzlich nur um die Methode peek() und das gesamte Konstrukt (die anderen Methode bitte nicht beachten, ist sowieso noch alles falsch). Ich möchte erst einmal verstehen, wie ich mit dieser ArrayList grundsätzlich arbeiten soll. wenn ich zum Beispiel den Befehl "System.out.println(a.peek());" ausführen möchte, funktioniert das nicht, weil "peek()" nicht für "ArrayList<Integer>" definiert worden ist. Und meine Frage ist, wie kann ich den nun die Methode "peek()" denn noch schreiben, um beispielsweise auf das erste oder auch letzte Element zuzugreifen, ohne den Rückgabewert zu verändern. Die einzelnen Methode sind mir auch noch durch das Interface gegeben worden. Hier das Interface:

Java:
import java.util.NoSuchElementException;

public interface PQInterface<E> {

    /**
    * @return the number of elements in the priority queue.
    */
    public int size();

    /**
    * @return true if the priority queue contains no elements.
    */
    public boolean isEmpty();

    /**
    * Adds element with given priority to the priority queue.
    * Should have O(log n) runtime complexity.
    *
    * @param element
    * @param priority
    */
    public void add(E element, int priority);

    /**
    * Returns the element with the highest priority, but does not remove it.
    * Should have O(1) runtime complexity.
    *
    * @return the element with the highest priority
    * @throws NoSuchElementException if the queue is empty
    */
    public E peek();

    /**
    * Removes the element with the highest priority and returns it.
    * Should have O(log n) runtime complexity.
    *
    * @return the element with the highest priority
    * @throws NoSuchElementException if the queue is empty
    */
    public E remove();

}

Das mag vielleicht jetzt alles auf einmal viel erscheinen, doch ich möchte nur zum Verständnis wissen, ob ich denn nun den Konstruktor ändern muss oder "private ArrayList<Entry> entryList;". Den Rest zu schreiben ist grundsätzlich nicht schwer und die Aufgabe an sich leicht, bloß ich verstehe nicht, wie ich das Grundgerüst schreiben soll, um überhaupt anfangen zu können. Danke im Voraus.

P.S.: Tut mir leid, dass es ein derart langer Post ist und sehr viel Informationen vorhanden sind, bloß ich poste lieber sehr viel Information rein, um vorläufige Fragen zu klären.
 

Anhänge

Zuletzt bearbeitet:
Arbeite besser noch mal die vorherigen Aufgaben durch. Du kannst dir, bei all den Lücken zu den OOP-Grundlagen, ja gar keine Gedanken über die eigentliche Aufgabe machen. Da steht so viel Murks in deinem Code, dass ich gar nicht weiß, womit ich mit einer Erklärung anfangen könnte. Am besten schmeißt du den hier geposteten Code ganz in die Tonne und fängst noch mal von vorne an.
Das ist übrigens ein ernst gemeinter Ratschlag 😕
 
Wieso denn? Nochmal, die ganzen Methoden (bis auf peek()) bitte nicht beachten. Die sind falsch, ich weiß. Mir geht es nur um das grundsätzliche. Den Konstruktor, die Attribute und die peek()-Methode, ob die einwandfrei sind und ob sie mit der Aufgabenstellung vereinbar sind. An dem Rest werde ich noch arbeiten, mir geht es nur vorerst um die drei Dinge. Danke im Voraus.
 
Wieso legst du in der main-Methode eine ArrayList an? Du willst doch sicherlich deine Implementierung des PQInterface testen, oder?
Was soll "EntryType" sein?
Mit einer ArrayList als internem Datenspeicher wirst du zudem nicht weit kommen. Damit sind die Anforderungen nicht erfüllbar.
 
Hast du die Aufgabenstellung überhaupt gelesen? Oder überflogen? Ich zitiere: "Die Knoten im Heap werden durch Objekte der Klasse Entry reprasentiert. Ein Entry ist
gegeben durch einen Wert (value) vom generischen Typ EntryType sowie eine Prioritat
(prio), gegeben als ganzzahliger Wert (int). Implementieren Sie Entry mit den zugehorigen
Attributen und passender Sichtbarkeit als innere Klasse von PriorityQueue."
Ups, jetzt verstehe ich den Fehler. Ich hätte einfach eine neue "PriorityQueue" anlegen sollen und keine "ArrayList", herzlichen Dank.
 
Hast du die Aufgabenstellung überhaupt gelesen? Oder überflogen? Ich zitiere: "Die Knoten im Heap werden durch Objekte der Klasse Entry reprasentiert. Ein Entry ist
gegeben durch einen Wert (value) vom generischen Typ EntryType sowie eine Prioritat
(prio), gegeben als ganzzahliger Wert (int). Implementieren Sie Entry mit den zugehorigen
Attributen und passender Sichtbarkeit als innere Klasse von PriorityQueue."

Das ist schon klar, dafür musste ich die Aufgabenstellung auch nicht lesen ... 🙂
Aber gut, dann frage ich andersherum: Was ist dann bei dir "E" ?
Problem ist ganz einfach, dass du hier einen zweiten generischen Typ-Parameter einführst, der aber das selbe bedeuten soll. Ob du den nun E oder EntryType oder ET nennst, ist wurscht, aber du schmeißt das wild durcheinander.
 
Also, im Interface ist nur das "E" und ich war jetzt der Auffassung, dass "Entrytype" ein anderer generischer Datentyp ist. Da aber beides gleichzeitig keinen Sinn macht und das das Gleiche ist, habe ich es vorläufig einfach weggelassen und nur das "E" hingeschrieben. Falls du aber meinst, dass das "vom generischen Typ EntryType" eigentlich heißen soll, dass ich mir einfach einen generischen Datentyp aussuchen soll, bin ich nur um so mehr froh, weil es dann Sinn macht. Danke nochmal 🙂
 
Wie der Typ-Parameter im Interface heißt hat nichts damit zu tun, wie der Typ-Parameter in deiner Klasse heißt.

Deine Klassendeklaration sollte so aussehen:
Java:
public class PriorityQueue<EntryType> implements PQInterface<EntryType> {
    //...
}

Und anschließend solltest du dann auch konsequent "EntryType" statt "E" schreiben. Ob du den Typ-Parameter hier "E", "EntryType" oder "KuddelMuddel" nennst, ist prinzipiell egal. Wichtig ist nur, dass es konsistent ist. Nur die Aufgabenstellung gibt vor, dass der Typ hier "EntryType" heißen soll. Einen Grund dafür gibt es aber eigentlich nicht, da diese Bezeichnung ausschließlich innerhalb der Klasse verwendet wird.
 
Ich verstehe jetzt zwar nicht, aus welchem Grund, wir auf einmal "EntryType" verwenden sollen (obwohl wir uns meist an den Interfaces orientieren sollen), aber so soll es sein. Nochmal großes danke, jetzt kann ich wenigstens vernünftig das ganze zu Ende bringen
 
Ich weiß das sprengt vielleicht jetzt den Rahmen und ist eine zusätzliche Frage, aber da du den Überblick hast, hätte ich noch eine winzige Frage und dann stell ich auch keine Fragen mehr hierzu. Also, wenn ich den Typ-Parameter für die Klasse auf "EntryType" setze, wie sollte ich den Typ-Parameter für die "ArrayList" setzen um auf alle Attribute von "Entry" zuzugreifen? Wenn ich ihn auf "Entry" setze, kann ich der Liste keinen Wert hinzufügen. Wenn ich ihn aber "EntryType" setze kann ich auf die Attribute in der inneren Klasse "Entry" nicht zugreifen, weil dies nicht der Typ-Parameter der ArrayList ist. Danke im Voraus.
P.S.: casten funktioniert nicht, so erhalte ich eine Fehlermeldung
 
Die ArrayList sollte Elemente vom Typ "Entry" aufnehmen, das ist schon richtig.
(Naja, eigentlich solltest du hier gar keine ArrayList verwenden, da damit die Anforderungen hinsichtlich der Laufzeit-Komplexität gar nicht erfüllt werden können, aber wenn du es dennoch zunächst damit probieren möchtest, dann sollte die Liste wie gesagt Element vom Typ "Entry" aufnehmen. )

Wieso das bei dir nicht geht ist schwer zu sagen, da ich deinen aktuellen Programm-Code nicht kenne und du auch keinerlei Fehlermeldungen oder sonstiges postest, was du zu sehen bekommst.
 
Wirklich? Mit was sollte ich es denn dann machen? Ich zitiere die Aufgabestellung: "In dieser Aufgabe wird diese Liste mit Hilfe der Klasse ArrayList aus der Java Standardbibliothek realisiert." Also deswegen dachte ich, ich sollte es mit einer ArrayList machen. Ich darf ja keine Referenzzeiger verwenden.

Und ohne großartig viel Code zu enthüllen, hier ein ganz simpler Aufruf, den ich realisieren möchte:
Java:
@Override
    public void add(EntryType element, int priority) {
        if(isEmpty()) entryList.add(element);
Die Fehlermeldung: "The method add(PriorityQueue<EntryType>.Entry) in the type ArrayList<PriorityQueue<EntryType>.Entry> is not applicable for the arguments (EntryType)"

Wenn ich die gleiche Zeile ausführe und die ArrayList mit dem Typ-Parameter "EntryType" instanziiert ist, dann funktionierts. Mit dem Typ-Parameter "Entry" funktioniert es, wie gesagt nicht. Danke im Voraus.
 
Wirklich? Mit was sollte ich es denn dann machen? Ich zitiere die Aufgabestellung: "In dieser Aufgabe wird diese Liste mit Hilfe der Klasse ArrayList aus der Java Standardbibliothek realisiert." Also deswegen dachte ich, ich sollte es mit einer ArrayList machen. Ich darf ja keine Referenzzeiger verwenden.
So genau hab ich mir den Aufgabentext tatsächlich nicht angeschaut. Prinzipiell kann man das ganze ja auch mit einer ArrayList lösen, aber man wird damit nicht die geforderten Laufzeit-Schranken einhalten können. Das geht ganz einfach nicht. Da braucht es einen balancierten Baum.


Und ohne großartig viel Code zu enthüllen, hier ein ganz simpler Aufruf, den ich realisieren möchte:
Java:
@Override
    public void add(EntryType element, int priority) {
        if(isEmpty()) entryList.add(element);
Die Fehlermeldung: "The method add(PriorityQueue<EntryType>.Entry) in the type ArrayList<PriorityQueue<EntryType>.Entry> is not applicable for the arguments (EntryType)"

Du übergibst an die Methode ein Objekt vom Typ "EntryType", das ist auch richtig so. Die Liste nimmt aber Objekte vom Typ "Entry" auf, was auch richtig ist. Du musst der Liste also auch ein Objekt vom Typ "Entry" hinzufügen. Dieses musst du nun aus den an die Methode übergebenene Werten für das Element und die Prio noch erstellen.
Also in etwa so:
Java:
public void add(EntryType element, int priority) {
    Entry entry = new Entry(element, prio);
    if(isEmpty()) {
        entryList.add(entry);
    }
    // ....
 
Das könnte ich dann nochmal morgen fragen, wegen der Laufzeitkomplexität. Und ich glaube, ich muss an die frische Luft ... NATÜRLICH muss ich ein neues Entry erstellen ... tausendmal gemacht und dennoch führe ich mich immer noch auf, wie ein Anfänger. Dennoch herzlichen Dank für alles 🙂
 

Zurück
Oben