Suche Hilfe zu doppelt verketteter Liste



  • Hallo Liebe Community.

    Ich habe mich seit kurzem für einen Fernlehrgang für C++ angemeldet, da es aber auf dem Online Studienzentrum recht wenig Beteiligung für diesen Lehrgang gibt wollte ich versuchen mein Wissen hier zu erweitern oder ebn Hilfe zu bekommen wenn ich das Gefühl habe fest zu stecken was gerade jetzt der Fall ist.

    Ich soll für den Lehrgang eine Einsendeaufgabe bearbeiten.
    Die Aufgabe klang für mich erstmal recht simpel.
    Ich muss eine doppelt verkettete Liste programmieren und diese dann sowohl vorwärts als auch rückwärts wieder ausgeben lassen .

    Ich programmiere erst seit 4 Wochen habe daher noch nicht die Größte Erfahrung und möchte mich schon mal im Vorfeld entschuldigen für eventuelle "dumme" Fragen.

    Ich bin keinesfalls an einer Komplettlösung interessiert da ich das Programmieren erlernen möchte und ich selber nix davon habe wenn andere das für mich machen.

    Das Problem liegt darin das wenn ich das Programm durch den Compiler jage ich keine Fehlermeldung bekomme da soweit alles syntaktisch richtig zu sein scheint.
    Seit ein paar Tagen grübel ich jetzt aber darüber nach warum mir bei der Rückwärtsausgabe nur die Elemente 4 und 3 angezeigt werden.
    Im Großen und ganzen ist es mir klar da der Zeiger der auf das Element 3 zeigt den wert nullptr besitzt.
    Jedoch bekomme ich es nicht hin das auch das Element 2 und 1 ausgegeben werden.
    Irgendwo habe ich einen Denkfehler drin und ich bin so festgefahren das ich ihn nicht finde.

    Ich bin für jede Hilfe dankbar
    Mit den besten Grüßen

    Snougins

    Zunächst aber erstmal mein Code:
    (nicht erschrecken die Vielen Kommentare dienen mir selber noch zum besseren Verständnis der einzelnen Codezeilen)

    Manchmal ist es strange seit Tagen rätsel ich daran und gerade viel es mir wie schuppen von den Augen.
    Vielen Dank trotzdem den code nehm ich mal raus vielen Dank trotzdem



  • Welchen Sinn hat last?
    Für eine doppelt verkettet Liste sollte es neben next ein prev (Vorgänger) geben.

    Immerhin verwendest du std::string, aber das Ganze sieht sehr nach C und nicht nach C++ aus. Vielleicht hat die geringe Beteiligung einen Grund ...


  • Mod

    manni66 schrieb:

    Welchen Sinn hat last?
    Für eine doppelt verkettet Liste sollte es neben next ein prev (Vorgänger) geben.

    Ich vermute einen Übersetzungsfehler von 'Letzter' im Sinne von 'Vorheriger' auf 'last', 'Letzter' im Sinne von 'Hinterster'.



  • Ja die wahl von last war nicht clever da gebe ich euch recht werde das auch noch in prev ändern.

    Mir ist die Lösung für mein Problem gerade ins Gesicht gesprungen.

    Vielen dank trotzdem.



  • Hallo Snougins,

    zunächst mal vorweg: Ich unterstelle, Du hast diese Aufgabe von den Veranstaltern des Fernlehrgangs bekommen. Wenn dem so ist, so ist das IMHO kein guter Lehrgang! Vielelicht für C, aber nicht für C++. Wobei es wahrscheinlich schwierig sein wird einen besseren zu finden.

    ich habe Deinen Fehler gar nicht gesucht, sondern nur ein paar Korrekturen vorgenommen, die den Code mehr C++'like machen. Die Änderungen habe ich mit '<==' markiert. Das hat den Code auch wesentlich gekürzt. Und fehlerhaft scheint er dann auch nicht mehr zu sein ..

    Gruß Werner

    PS.: die dümmsten Programmierer schreiben die dicksten Programme

    /*########################################
    Einsendeaufgabe 5.2
    ########################################*/
    #include <iostream>
    #include <string>       // <=== fehlte
    #include <cassert>
    using namespace std;
    
    //Die Struktur für die Listenelemente
    struct listenelement
    {
        listenelement( const string& d, listenelement* n, listenelement* l )
            : daten( d )
            , next( n )
            , last( l )
        {}  // <=== ein Konstruktor ist der Initialiserung der Member durch den Aufrufer IMMER vorzuziehen
    
        string daten;
        listenelement *next;
        listenelement *last;
    };
    
    //Eine Funktion zum Anhängen von Elementen an die Liste
    void anhaengen(string datenneu, listenelement *listenanfang)
    {
        assert( listenanfang ); // <=== listenanfang darf nicht nullptr sein!
    
        //ein lokaler Hilfszeiger, um in der Liste wander zu können
    
        //ein lokaler Hilfszeiger um das Ende der Liste zu sichern
    
        //den Hilfszeiger an den Anfang der Liste setzen
        listenelement* hilfszeiger = listenanfang;  // <=== Deklaration und Initialisierung sollte möglichst eins sein
        //Durch die Liste gehen bis das letzte Element erreicht ist
        while (hilfszeiger->next != nullptr)
        {
            hilfszeiger =hilfszeiger->next;
        }
    
        hilfszeiger->next = new listenelement( datenneu, nullptr, hilfszeiger );    // <=== das ist alles
    
        //hier wird der Hilfszeiger auf hilfszeigerEnde gesetzt um den Vorgänger nicht zu vergessen
        // ... Rest gelöscht
    }
    
    //eine Funktion zum Ausgeben aller Listenelemente rückwärts
    void ausgabeBACKWARDS(listenelement *listenende)
    {
        cout<<"Hier wird die Liste rueckwaerts ausgegeben." <<'\n';
    
        // .. gelöscht
    
        // cout<<hilfszeiger->daten <<'\n';    // <== redundanten Code vermeiden (s.u.)
        //solange das Ende der Liste noch nicht erreicht ist:
        for( listenelement* hilfszeiger = listenende; hilfszeiger != nullptr; hilfszeiger= hilfszeiger->last )
        // while (hilfszeiger->last !=nullptr) // <== for ist besser als while
        {
            //Daten ausgeben
            cout<<hilfszeiger->daten <<'\n';
        }
    }
    
    //eine Funktion zum Ausgeben aller Listenelemente
    void ausgeben (listenelement *listenanfang)
    {
        // ... gelöscht
        listenelement* listenEnde = nullptr; // <== (s.u.)
    
        // cout<<hilfszeiger->daten <<'\n'; // <=== Redundanzen vermeiden!
        //solange das Ende der Liste noch nicht erreicht ist:
        for( listenelement *hilfszeiger = listenanfang; hilfszeiger != nullptr; hilfszeiger = hilfszeiger->next )   // for besser als while
        // while (hilfszeiger->next != nullptr)
        {
            //Daten ausgeben
            cout<< hilfszeiger->daten <<'\n';
            listenEnde = hilfszeiger; // <===  aktuelles Element merken
        }
    
        //da nach dieser Ausgabe der der Hilfszeiger bereits auf dem Ende der Liste steht übergebe ich diesen als Argument an die Funktion rueckausgabe
        ausgabeBACKWARDS(listenEnde);
    }
    
    //die Liste Leeren und Speicher freimachen
    void ende(listenelement *listenanfang)
    {
        //ein lokaler Hilfszeiger um in der Liste wandern zu können
        listenelement *hilfszeiger;
        //solange noch Elemente in der Liste sind :
        while( listenanfang !=nullptr)
        {
            //Den Hilfszeiger auf das Erste Element der Liste setzen
            hilfszeiger = listenanfang;
            //Den Zeiger für dden Listenanfang auf das nächste Element setzen
            listenanfang = listenanfang->next;
            //den Speicher für das herausgenommene Element wieder freigeben
            delete(hilfszeiger);
        }
    }
    
    int main()
    {
        // <== ein Zeiger auf den Anfang der Liste UND das erste Element erzeugen
        listenelement *listenanfang =new listenelement( "Element 1", nullptr, nullptr ); // <=== Konstruktor benutzen
        //Daten in das erste Element schreiben
        // listenanfang->next =nullptr;
        // listenanfang->daten= "Element 1"; // <=== s. listenelement Konstruktor
    
        //und jetzt ein weiteres ELement erzeugen
        anhaengen("Element 2", listenanfang);
        anhaengen("Element 3", listenanfang);
        anhaengen("Element 4", listenanfang);
    
        cout<<"Hier wird die Liste Vorwaerts ausgegeben." <<'\n';
        //die Liste ausgeben
        ausgeben(listenanfang);
        //die Liste wieder abbauen
        ende(listenanfang);
    
        return 0;
    }
    


  • Hey vielen Dank Werner,

    ich werde mir deine Änderungen mal genau anschauen und mir die Ratschläge zu gemühte führen.
    Wie gesagt da ich noch ganz am Anfang stehe und ich noch keine Erfahrungen im Umgang mit Programmiersprachen habe danke ich dir sehr für die ganzen Hinweise.
    Denn schließlich will ich ja das auch andere meinen Code lesen können daher danke und ich werde mir hoffentlich mit der Zeit einiges in punkto Programmierstil aneignen können sodass meine Codes übersichtlicher und besser zu lesen sind.

    Beste Grüße

    Snougins



  • Hallo Snougins,
    ich habe das Programm einmal in eine C++'like Form umgestellt. Eine der wesentlichen Grundlagen von C++ ist der Gebrauch von Kon- und Destruktor - oder wie Bjarne Stroustrup auch sagt: "resource acquisition is initialization". Schau es Dir an und falls Du Fragen hast, so nur heraus damit.

    Gruß Werner

    #include <iostream>
    #include <string>
    
    class Liste
    {
        // Die Struktur für die Listenelemente
        struct listenelement
        {
            listenelement( const std::string& d, listenelement* n, listenelement* l )
                : daten( d )
                , next( n )
                , last( l )
            {}  // <=== ein Konstruktor ist der Initialisierung der Member durch den Aufrufer IMMER vorzuziehen
    
            std::string daten;
            listenelement *next;
            listenelement *last;
        };
    
    public:
        Liste()
            : anker_( std::string(), &anker_, &anker_ )
        {}
    
        ~Liste()
        {   // das Aufräumen geschieht automatisch im Destruktor
            for( listenelement* elem = anker_.next; elem != &anker_; )
            {
                listenelement* temp = elem;
                elem = elem->next;
                delete temp;
            }
            anker_.last = &anker_;
            anker_.next = &anker_;
        }
    
        ///////////////////////////////////////////////////////////////
        // Methode 'anhaengen' - aus:
        //      +--------+          +--------+  
        //      | Elem X +-anker->  | anker_ +-->
        //   <--+        |   <-last-+        |  
        //      +--------+          +--------+  
        //
        // ... wird:
        //       +--------+         +--------+          +--------+  
        //       | Elem X +--neu->  |  neu   +-anker->  | anker_ +-->
        //   <---+        |   <-X --+        |    <-neu-+        |  
        //       +--------+         +--------+          +--------+  
        //
        void anhaengen( const std::string& daten )
        {
            listenelement* neu = new listenelement( daten, &anker_, anker_.last );
            anker_.last->next = neu;
            anker_.last = neu;
        }
    
        // ausgeben
        friend std::ostream& operator<<( std::ostream& out, const Liste& lst );
    
    private:
        listenelement anker_;
    
        Liste( const Liste& ) = delete;
        Liste& operator=( const Liste& ) = delete;
    };
    
    namespace
    {
        const auto LISTE_PRINT_FLAG = std::ios_base::xalloc();
    
        std::ostream& revers( std::ostream& out )
        {
            out.iword(LISTE_PRINT_FLAG) = 1;
            return out;
        }
    }
    
    // ausgeben (vorwärts oder rückwärts)
    std::ostream& operator<<( std::ostream& out, const Liste& lst )
    {
        auto& flg = out.iword(LISTE_PRINT_FLAG);
        if( flg != 0 )
        {
            for( auto elem = lst.anker_.last; elem != &lst.anker_; elem = elem->last )
            {
                out << elem->daten << '\n';
            }
            flg = 0; // Flag rücksetzen
        }
        else
        {
            for( auto elem = lst.anker_.next; elem != &lst.anker_; elem = elem->next )
            {
                out << elem->daten << '\n';
            }
        }
        return out;
    }
    
    int main()
    {
        using namespace std;
        Liste liste;    // eine leere(!) Liste
    
        liste.anhaengen("Element 1");
        liste.anhaengen("Element 2");
        liste.anhaengen("Element 3");
        liste.anhaengen("Element 4");
        cout << "Hier wird die Liste Vorwaerts ausgegeben.\n" << liste << revers << "Hier wird die Liste rueckwaerts ausgegeben.\n" << liste;
        return 0;
    }
    

Log in to reply