Doppelt verkettete Liste

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Doppelt verkettete Liste

    Hallo, :)

    Vorab erst mal, ich weiss dass es zig Möglichkeiten gibt verkettete Listen zu bauen, selbstverständlich auch nur mit Klassen als Container usw. mir geht es aber hier ums Verständnis der hier benutzten Art von verketteten Listen.
    Das Problem ist wahrscheinlich sehr gering aber ich dreh mich schon seit Tagen um mich selbst und find den Bug nicht!

    Was Funktioniert: :wink:
    1. Die Liste wird korrekt angelegt und ausgegeben auch die Anzahl der Elemente.
    2. Das neue erste Element wird korrekt angelegt und die Liste wird ebenso korrekt ausgegeben auch die Anzahl der Elemente.


    Was nicht funktioniert: :(
    Das neue letzte Element wird wahrscheinlich nicht richtig angelegt und deshalb wird das letzte Element nicht ausgegeben wenn die Liste ausgegeben wird.

    Wird jedoch der obere Teil in der Funktion addNewLastElement(int wert) einkommentiert,
    in diesem Teil füge ich das letzte Element wie bei einer einfach verketteten Liste ein dann funktioniert alles.

    Ich gehe übrigens davon aus dass sich schon ein Element in der Liste befindet (keine Plausibilitätsprüfung) da ich die Liste ja zuvor erstellt habe.
    Ich habe versucht alles so gut und so verständlich wie möglich zu kommentieren.

    Hier der Code für die main():

    Quellcode

    1. #include "List.h"
    2. void main()
    3. {
    4. mvList mvListe;
    5. int i;
    6. int j = 1;
    7. cout << "Liste ist leer !!! ==> Liste mit 3 Elementen bauen" << endl;
    8. for (i = 0; i < 3; i++)
    9. {
    10. mvListe.addElement(j++);
    11. }
    12. mvListe.showElements();
    13. cout << "Neues Erstes Element einfuegen" << endl;
    14. mvListe.addElement(5, FIRST);
    15. mvListe.showElements();
    16. cout << "Neues letztes Element einfuegen" << endl;
    17. mvListe.addElement(10, LAST);
    18. mvListe.showElements();
    19. }
    Alles anzeigen


    Hier der Code für die List.h:

    Quellcode

    1. #include <iostream>
    2. using namespace std;
    3. enum { NONE = 0,FIRST, LAST, MIDDLE };
    4. class mvList
    5. {
    6. private:
    7. //*************************************** SRTUCT ERSTELLEN *************************************
    8. struct Listenknoten
    9. {
    10. int data;
    11. Listenknoten *next;
    12. Listenknoten *prev;
    13. };
    14. public:
    15. //******************************* ZEIGER AUF STRUCT ERSTELLLEN *********************************
    16. Listenknoten *neu;
    17. Listenknoten *head;
    18. Listenknoten *tail;
    19. Listenknoten *current1;
    20. Listenknoten *current2;
    21. int counterElements;
    22. //********************************** KONSTRUKTOR / DESTRUKTOR **********************************
    23. mvList()
    24. {
    25. neu = NULL; // Zeiger mit Null initialisieren
    26. head = NULL; // Zeiger mit Null initialisieren
    27. tail = NULL; // Zeiger mit Null initialisieren
    28. current1 = NULL; // Zeiger mit Null initialisieren
    29. current2 = NULL; // Zeiger mit Null initialisieren
    30. counterElements = 0;
    31. }
    32. ~mvList()
    33. {
    34. neu = NULL; // Zeiger mit Null initialisieren
    35. head = NULL; // Zeiger mit Null initialisieren
    36. tail = NULL; // Zeiger mit Null initialisieren
    37. current1 = NULL; // Zeiger mit Null initialisieren
    38. current2 = NULL; // Zeiger mit Null initialisieren
    39. delete neu;
    40. delete head;
    41. delete tail;
    42. delete current1;
    43. delete current2;
    44. }
    45. //******************************* PROTOS DER MEMBERFUNKTIONEN **********************************
    46. void showElements();
    47. void addElement(int wert, int , int);
    48. void addNewFirstElement(int wert);
    49. void addNewLastElement(int wert);
    50. void addNewMiddleElement(int wert, int atPosition);
    51. };
    52. //**********************************************************************************************
    53. //******************************* ENTSCHEIDUNG FUNKTIONAUFRUFE *********************************
    54. void mvList::addElement(int wert, int atPos = NONE, int pos = 0)
    55. {
    56. if (atPos != NONE)
    57. {
    58. switch(atPos)
    59. {
    60. case FIRST: addNewFirstElement(wert);
    61. break;
    62. case LAST: addNewLastElement(wert);
    63. break;
    64. default:
    65. break;
    66. }
    67. }else
    68. {
    69. addNewFirstElement(wert);
    70. }
    71. }
    72. //**********************************************************************************************
    73. //****************************** NEUES ERSTES ELEMENT EINFÜGEN *********************************
    74. void mvList::addNewFirstElement(int wert)
    75. {
    76. neu = new Listenknoten;
    77. neu->prev = NULL;
    78. neu->next = head;
    79. head = neu;
    80. neu->data = wert;
    81. counterElements++;
    82. cout << "Elemente: " << counterElements;
    83. }
    84. //**********************************************************************************************
    85. //************************* NEUES LETZTES ELEMENT EINFUEGEN INSERT AFTER ***********************
    86. void mvList::addNewLastElement(int wert)
    87. {
    88. neu = new Listenknoten;
    89. //
    90. //
    91. // current1 = head; // einkommentieren
    92. // while (current1->next != NULL) // einkommentieren
    93. // { // einkommentieren
    94. // current1 = current1->next; // einkommentieren
    95. // } // einkommentieren
    96. // neu->next = NULL; // einkommentieren
    97. // // einkommentieren
    98. // current1->next = neu; // einkommentieren
    99. // // einkommentieren
    100. // neu->data = wert; // einkommentieren
    101. neu->next = NULL; // auskommentieren
    102. neu->prev = tail; // auskommentieren
    103. tail = neu; // auskommentieren
    104. neu->data = wert; // auskommentieren
    105. counterElements++;
    106. cout << "Elemente: " << counterElements;
    107. }
    Alles anzeigen


    Vorab schon mal vielen Dank für eure Mühe
    ShadowEater :wink:
  • also ich hab nur mal schnell drüber geschaut und da is mir was ins Auge gefallen:
    du rufst

    Quellcode

    1. mvListe.addElement(5, FIRST);
    mit zwei parametern auf.
    der Prototyp sieht so aus:

    Quellcode

    1. void addElement(int wert, int , int); //(3 Parameter)

    und die ganze Methode:

    Quellcode

    1. void mvList::addElement(int wert, int atPos = NONE, int pos = 0)
    2. {
    3. if (atPos != NONE)
    4. {
    5. //...

    und das frisst dein Compiler??
  • Hallo!

    Erst mal etwas kurzes Programmiertechnisches:
    1. Versuch dir die 8 Einrückungen abzugewöhnen und statt dessen 2 oder 4 Zeichen zu nehmen, da du sonst bei komplexen Programmen leicht den Überblick verlierst (ist aber subjektiv, und dass ist nur meine Meinung).
    2. Im Destruktor von mvList setzt du die Variablen erst auf NULL und rufst dann delete auf - das kann nicht funktionieren, da durch das NULL setzen ja nix mehr drin steht. Lösung: einfach das NULL setzen weg lassen
    3. "neu", "current1" und "current2" sollen keine Membervariablen sein (bei der Klasse definiert) sondern in der Methode, dort wo du sie brauchst.
    Außerdem führt das Löschen dort höchstwahrscheinlich zu einer Zugriffsverletzung (Absturz).
    4. Du löscht im Destruktor nur die 5 Variablen (wovon die 3 oben genannten nicht dort hin gehören), nicht aber die Listenelemente. Daher bleibt dir Speicher übrig (Speicherleak). Lösung:

    Quellcode

    1. virtual ~myList () {
    2. Listenknoten *p = head;
    3. while (p != NULL)
    4. {
    5. Listenknoten *next = p->next; // holen, bevor "p" gelöscht wird!!
    6. delete p; // Speicher freigeben
    7. p = next;
    8. }
    9. }



    Folgenden "Spezialfall" hast du übersehen:
    Wenn addNewFirstElement zum ersten mal aufgerufen wird, musst du sowohl head als auch tail setzen, da das erste Element Anfang und Ende ist! Für addNewLastElement gilt das naatürlich auch.


    Und dein Hauptfehler ist der, dass du einfach immer head und tail neu belegst, aber die vorherigen "head" und "tail" Objekte nicht anpasst:

    Quellcode

    1. // Vergessen: setzen des Vorgängers des "alten" heads
    2. if (head != NULL)
    3. head->prev = neu;
    4. head = neu;

    bzw.

    Quellcode

    1. // Vergessen: setzen des Nachfolgers des "alten" tails
    2. if (tail != NULL)
    3. tail->next = neu;
    4. tail = neu;


    Hierzu ein kleines Beispiel, beginnend bei einer leren Liste, anhand der einzelnen modifizierten Listenknoten:
    addFirst(3):

    Quellcode

    1. [1] neu={3,NULL,NULL)
    2. [2] head={3,NULL,NULL}
    3. [3] tail={3,NULL,NULL)


    addFirst(2):

    Quellcode

    1. [1] neu={2,NULL,head}
    2. [2] head={3,head,NULL}
    3. [3] head=neu --> head={2,NULL,{3,...}}
    4. Zur Info: tail={3,head,NULL} siehe Aktion [2]


    usw. Mir ist klar, dass das nicht ganz intuitiv ist, aber denk mal drüber nach und versuchs zu behirnen. Du kannst das am einfachsten verifizieren, wenn du die Liste einmal von vorne nach hinten durchgehst und schaust, ob genau die umgekehrte Reihenfolge rauskommt wie wenn du von vorne nach hinten durchgehst (siehe Code).

    Adaptiertest List.h:

    Quellcode

    1. #include <iostream>
    2. using namespace std;
    3. enum { NONE = 0,FIRST, LAST, MIDDLE };
    4. class mvList
    5. {
    6. private:
    7. //*************************************** SRTUCT ERSTELLEN *************************************
    8. struct Listenknoten
    9. {
    10. int data;
    11. Listenknoten *next;
    12. Listenknoten *prev;
    13. };
    14. public:
    15. //******************************* ZEIGER AUF STRUCT ERSTELLLEN *********************************
    16. Listenknoten *head;
    17. Listenknoten *tail;
    18. int counterElements;
    19. //********************************** KONSTRUKTOR / DESTRUKTOR **********************************
    20. mvList()
    21. {
    22. head = NULL; // Zeiger mit Null initialisieren
    23. tail = NULL; // Zeiger mit Null initialisieren
    24. counterElements = 0;
    25. }
    26. ~mvList()
    27. {
    28. Listenknoten *p = head;
    29. while (p != NULL)
    30. {
    31. Listenknoten *next = p->next; // holen, bevor "p" gelöscht wird!!
    32. delete p; // Speicher freigeben
    33. p = next;
    34. }
    35. // tail zeigt auf gelöschten Speicher!
    36. }
    37. //******************************* PROTOS DER MEMBERFUNKTIONEN **********************************
    38. void addElement(int wert, int , int);
    39. void addNewFirstElement(int wert);
    40. void addNewLastElement(int wert);
    41. void showElements ()
    42. {
    43. cout << "Vorne nach hinten: ";
    44. Listenknoten *p = head;
    45. while (p)
    46. {
    47. cout << p->data;
    48. if (p->next)
    49. cout << ", ";
    50. p = p->next;
    51. }
    52. cout << endl;
    53. cout << "Hinten nach vorne: ";
    54. p = tail;
    55. while (p)
    56. {
    57. cout << p->data;
    58. if (p->prev)
    59. cout << ", ";
    60. p = p->prev;
    61. }
    62. cout << endl;
    63. }
    64. };
    65. //**********************************************************************************************
    66. //******************************* ENTSCHEIDUNG FUNKTIONAUFRUFE *********************************
    67. void mvList::addElement(int wert, int atPos = NONE, int pos = 0)
    68. {
    69. if (atPos != NONE)
    70. {
    71. switch(atPos)
    72. {
    73. case FIRST: addNewFirstElement(wert);
    74. break;
    75. case LAST: addNewLastElement(wert);
    76. break;
    77. default:
    78. break;
    79. }
    80. }else
    81. {
    82. addNewFirstElement(wert);
    83. }
    84. }
    85. //**********************************************************************************************
    86. //****************************** NEUES ERSTES ELEMENT EINFšGEN *********************************
    87. void mvList::addNewFirstElement(int wert)
    88. {
    89. Listenknoten *neu = new Listenknoten;
    90. neu->prev = NULL;
    91. neu->next = head;
    92. neu->data = wert;
    93. // Vergessen: setzen des Vorgängers des "alten" heads
    94. if (head != NULL)
    95. head->prev = neu;
    96. head = neu;
    97. // Neu:
    98. if (tail == NULL)
    99. tail = head;
    100. counterElements++;
    101. cout << "Elemente: " << counterElements << endl;
    102. }
    103. //**********************************************************************************************
    104. //************************* NEUES LETZTES ELEMENT EINFUEGEN INSERT AFTER ***********************
    105. void mvList::addNewLastElement(int wert)
    106. {
    107. Listenknoten *neu = new Listenknoten;
    108. neu->next = NULL;
    109. neu->prev = tail;
    110. neu->data = wert;
    111. // Vergessen: setzen des Nachfolgers des "alten" tails
    112. if (tail != NULL)
    113. tail->next = neu;
    114. tail = neu;
    115. // Neu:
    116. if (head == NULL)
    117. head = tail;
    118. counterElements++;
    119. cout << "Elemente: " << counterElements << endl;
    120. }
    Alles anzeigen


    Roman-Ende