[Code][C/C++] Einfach verkettete Liste

Übersicht Andere Programmiersprachen Allgemein

Neue Antwort erstellen

 

CO2

ehemals "SirMO"

Betreff: [Code][C/C++] Einfach verkettete Liste

BeitragDi, Feb 19, 2013 17:24
Antworten mit Zitat
Benutzer-Profile anzeigen
Hallo,
Zunächst: Wuhuu erster Post im neuen Forum Cool

UPDATE 2 - 19.02.2013, 21:34

Zum Code: Dieser entstand für ein Projekt, hat bisher auch immer seinen Dienst getan Wink

simplelist.h Code: [AUSKLAPPEN]
/*
    Autor: Marius Otto
    Datum: 19.02.2013
    Titel: SimpleList.H
*/

#ifndef SIMPLELIST_INCLUDED
#define SIMPLELIST_INCLUDED

#include <stdlib.h>

struct Liste
{
    int Index;
    void* Data;
    struct Liste* NextElement;
};

void ListAddLast(struct Liste**, void*, int);
void* ListGetElement(struct Liste*, int);
int ListRemoveElement(struct Liste*, int);
int ListCountElements(struct Liste*);
void ListChangeIndex(struct Liste*, int, int);

#endif


simplelist.c Code: [AUSKLAPPEN]
/*
    Autor: Marius Otto
    Datum: 19.02.2013
    Titel: SimpleList.C
*/

#include "simplelist.h"

void ListAddLast(struct Liste** List, void* Data, int Index)
{
    struct Liste *NewElement;
    struct Liste *TempList = *List;

    NewElement = (struct Liste*)malloc(sizeof(*NewElement));
    NewElement->Data = Data;
    NewElement->Index = Index;
    NewElement->NextElement = NULL;

    if(TempList != NULL)
    {
        while(TempList->NextElement != NULL)
        {
            TempList = TempList->NextElement;
        }
        TempList->NextElement = NewElement;
    }
    else
    {
        *List = NewElement;
    }
}

void* ListGetElement(struct Liste* List, int Index)
{
    struct Liste* TempList = List;
    while(TempList != NULL)
    {
        if(TempList->Index == Index)
        {
            return TempList->Data;
        }
        TempList = TempList->NextElement;
    }
    return NULL;
}

int ListRemoveElement(struct Liste* List, int Index)
{
    struct Liste* TempList = List;
    struct Liste* TempPrevList;
    while(TempList != NULL)
    {
        if(TempList->Index == Index)
        {
            TempPrevList->NextElement = TempList->NextElement;
            return 1;
        }
        TempPrevList = TempList;
        TempList = TempList->NextElement;
    }
    return -1;
}

int ListCountElements(struct Liste* List)
{
    struct Liste* TempList = List;
    if(TempList != NULL)
    {
        int Count = 0;
        while(TempList != NULL)
        {
            Count++;
            TempList = TempList->NextElement;
        }
        return Count;
    }
    else
    {
        return -1;
    }
}

void ListChangeIndex(struct Liste* List, int OldIndex, int NewIndex)
{
    struct Liste* TempList = List;
    while(TempList != NULL)
    {
        if(TempList->Index == OldIndex)
        {
            TempList->Index = NewIndex;
        }
        TempList = TempList->NextElement;
    }
}


Zur Erklärung:
-> struct Liste: Dieses struct enthält zum einen einen Index, mit welchem die entsprechenden Daten angesprochen werden können. Zudem noch den eigentlichen Datenblock, welcher den Wert speichert. Hinzu kommt noch ein Zeiger auf ein struct Liste, welches das nächste Element der Liste enthält.
-> void ListAddLast(struct Liste** List, void* Data, int Index): Diese Funktion fügt der Liste ein weiteres Element hinzu. Parameter: 1.) struct Liste**: Ein Zeiger auf den Anfang der Liste. 2.) void* Data: Der Wert, welcher in diesem Element gespeichert werden soll (Typecast nötig). 3.) int Index: Index, über den das Element angesprochen werden kann.
-> void* ListGetElement(struct Liste* List, int Index): Diese Funktion liefert den Wert eines Elements der Liste zurück (Typecast nötig). Parameter: 1.) struct Liste* List: Der Anfang der Liste. 2.) int Index: Index, für welches Element der Liste der Wert zurückgegeben werden soll. Sollte es das entsprechende Element in der Liste nicht geben oder die Liste nicht existieren, so gibt die Funktion -1 zurück.
-> int ListRemoveElement(struct Liste* List, int Index): Diese Funktion löscht das Element der Liste, welches den angegebenen Index besitzt. Sollte dies erfolgreich sein, so gibt die Funktion eine 1 zurück, sonst eine -1.
-> int ListCountElements(struct Liste* List): Diese Funktion zählt alle Elemente einer Liste und gibt diese zurück. Sollte die Liste nicht vorhanden sein (= NULL), so gibt die Funktion -1 zurück.
-> void ListChangeIndex(struct Liste* List, int OldIndex, int NewIndex): Diese Funktion ändert den Index eines Elements der Liste.

ACHTUNG: Geänderte Version, hier müssen Typecasts vorgenommen werden, um brauchbare Daten zu erhalten!

Zum Schluss noch ein Testprogramm:
main.c (Testprogramm) Code: [AUSKLAPPEN]
#include <stdio.h>
#include <stdlib.h>
#include "simplelist.h"

int main()
{
    struct Liste* TestListe = NULL;
    ListAddLast(&TestListe, (void*)9, 0);
    ListAddLast(&TestListe, (void*)45, 1);
    ListAddLast(&TestListe, (void*)33, 2);
    ListAddLast(&TestListe, (void*)12, 3);

    int x;
    for(x = 0; x < ListCountElements(TestListe); x++)
    {
        printf("%d.) %d\n", x, (int)ListGetElement(TestListe, x));
    }
    printf("=========\n");
    ListRemoveElement(TestListe, 1);
    ListChangeIndex(TestListe, 2, 1);
    ListChangeIndex(TestListe, 3, 2);
    for(x = 0; x < ListCountElements(TestListe); x++)
    {
        printf("%d.) %d\n", x, (int)ListGetElement(TestListe, x));
    }
    return 0;
}


Das ganze gibt es natürlich auch als Download.
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti
  • Zuletzt bearbeitet von CO2 am Do, Feb 21, 2013 18:24, insgesamt 6-mal bearbeitet

Thunder

BeitragDi, Feb 19, 2013 18:26
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich mag mir jetzt echt nicht noch mehr Feinde machen in dem Forum Razz aber das ist irgendwie... weder quantitativ noch qualitativ überragend...

Im Endeffekt machst du sogar durch die Index-Variable den geringeren Speicherverbrauch der einfach verketteten Liste zunichte, denn der Index wird normalerweise nicht gespeichert, sondern beim Durchlaufen der Liste mitgezählt. Eine Bilderbuch-Implementation der doppelt verketteten Liste hätte statt der Index-Variablen einen zweiten Zeiger, was (zumindest bei 32bit) genau so viel wie eine Integer-Variable ist. Beim Insert wird ein Element normalerweise auch an die gegebene Index-Position eingefügt, und nicht ans Ende der Liste, mit einer Index-Variablen.

Darüberhinaus enthält soweit ich weiß die C++ Laufzeitbibliothek eine Template-Deklaration einer (doppelt) verketteten Liste. Du kannst also sogar jeden Datentyp darin speichern - ohne Änderungen.

Der Code hat schätzungsweise 30 Zeilen, auch wenn wir uns nicht auf diese Sprachen spezialisieren heißt es nicht, dass hier für jedes Nischenproblem eine Lösung da sein muss (besonders, wenn sie eben schon gelöst ist).
Ist aber nicht persönlich gemeint oder so... Rolling Eyes
Meine Sachen: https://bitbucket.org/chtisgit https://github.com/chtisgit
 

feider

ehemals "Decelion"

BeitragDi, Feb 19, 2013 19:35
Antworten mit Zitat
Benutzer-Profile anzeigen
Eine anschauliche einfach verkettete Liste, allerdings fehlt da noch ein bisschen was. Z.B. kann ich noch keine Elemente löschen. Schöner fände ich es auch, wenn statt eines int ein void* gespeichert werden würde - das würde das Ganze flexibler machen. Schön wäre auch noch eine einfache Möglichkeit, das Ganze durchzuiterieren und um zu überprüfen, ob ein Objekt / ein Zeiger in einer Liste vorhanden ist.

@Thunder:
C++ ist nicht C. OpenCL und Arduino Boards sind zum Beispiel ein Subset von C - da kannst du mit schönen STL-Objekten gar nichts anfangen.
Das Ziel einer Liste ist zudem nicht geringer Speicherverbrauch, sondern Dynamik.

Grüße
feider
 

CO2

ehemals "SirMO"

BeitragDi, Feb 19, 2013 20:44
Antworten mit Zitat
Benutzer-Profile anzeigen
Zunächst danke für die Antworten.

@ Thunder: Zitat:
geringeren Speicherverbrauch der einfach verketteten Liste zunichte
- wie feider schon richtig schreibt, geht es bei einer Liste nicht um Speicherverbrauch, sondern um Dynamik.

Zitat:
[...]soweit ich weiß die C++ Laufzeitbibliothek eine Template-Deklaration einer (doppelt) verketteten Liste.
- ich schrieb nur C++ in den Titel, weil jeder C++ Compiler auch C Quelltext übersetzen kann und es sich somit um eine Lösung für beides handelt. Das mit dem Template wusste ich nicht.

Zitat:
[...]denn der Index wird normalerweise nicht gespeichert, sondern beim Durchlaufen der Liste mitgezählt.
- Das hat durchaus Vorteile, aber auch Nachteile. Zunächst die Vorteile: Ich kann Elemente "zwischen" 2 Elementen hinzufügen, und der Index ist trotzdem noch fortlaufend. Andersrum können auch Elemente "zwischen" 2 Elementen gelöscht werden, für den Index gilt dasselbe wie oben.
Allerdings hat diese Methode auch einen riesigen Nachteil: Der Programmierer muss zu jeder Zeit wissen, an welchem Index sich die gewünschte "Resource" befindet (also auch nach Änderungen an der Liste). Und eben dieser Nachteil brachte mich dazu, explizit einen Index einzubauen. So wird am Anfang, wenn das Element zur Liste hinzugefügt wird, festgelegt, welchen Index dieses hat und kann dann nur noch über ListChangeIndex() geändert werden, nicht aber durch hinzufügen/entfernen von Elementen.

@ feider: Zitat:
[...]kann ich noch keine Elemente löschen
- Oh, dass ist mir im Eifer des Gefechts noch gar nicht aufgefallen... brauchte die Funktion bisher aber auch nicht, wird aber umgesetzt!

Zitat:
[...]wenn statt eines int ein void* gespeichert werden würde[...]
- Dafür müsste der Benutzer später immer einen expliziten Type-Cast durchführen, um brauchbare Daten zu erhalten. Natürlich ist dies möglich ich dachte aber an eine einfache Benutzung, d.h. der Benutzer muss sich nicht um Type-Casts kümmern.

@ All: Update im ersten Post!
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti

Thunder

BeitragDi, Feb 19, 2013 22:31
Antworten mit Zitat
Benutzer-Profile anzeigen
@feider: Minimalhardware ist ein Argument. Im selben Atemzug mein Speicherverbrauch-Argument herunterzumachen ist aber falsch. Und wenn du Mal eine Liste implementiert hast, weißt du, dass der Index nicht gespeichert wird.
Und das Argument mit dynamisch verstehe ich nicht. Die Liste ist so oder so eine dynamische Datenstruktur, ob man es jetzt so implementiert wie CO2 oder wie ich es gerne hätte Razz Andererseits gehört aber das Einfügen von Daten (in der Mitte z.B.) unbedingt zu den wichtigen Funktionen einer Liste, für mich ist, was hier präsentiert wird, eher ein Kellerspeicher.

Das mag man jetzt als Haarspalterei bezeichnen, aber ich finde es ganz schlecht, wenn etwas irreführend ist. Man kommt so schwer los von etwas, das man falsch gelernt hat.

Es sind jetzt aber auch schon mehr Funktionen drinnen, als wie ich den Post das erste Mal gelesen habe. Und ich werde mich auch nicht aufregen darüber (hatte ich nie groß vor), weil es immerhin Code ist, der seinen Zweck erfüllt (ohne es jetzt überprüft zu haben), und wir ihn hier archivieren wollen, dass er weiterverwendet werden kann. Und vielleicht braucht ihn ja jemand Mal.
Meine Sachen: https://bitbucket.org/chtisgit https://github.com/chtisgit
 

CO2

ehemals "SirMO"

BeitragDi, Feb 19, 2013 22:40
Antworten mit Zitat
Benutzer-Profile anzeigen
@ All: Gab ein weiteres Update... (Siehe obiger Post)
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti
 

c64

BeitragSo, Feb 24, 2013 18:21
Antworten mit Zitat
Benutzer-Profile anzeigen
*Grieeen !

Warum man die Redundanz eines Index in einer Liste durch die Dynamic einer solchen rechtfertigt/begründet ist, hmm ja ist ... mir ein Rätsel Laughing .


Als Übing Why Not!, man lernt den Umgang mit Pointern etc. und gerade solches Experimentieren bringt m.E. mit unter die notwendige Erfahrung/Gefühl fürs Programmieren ganz egal welche Sprache!

Dennoch mal als Anregung begründe doch mal den Nutzen deines Index! Vllt. merkst du dann doch das dieser hier nicht so passig/sinnvoll ist Wink !


mfg.
Patrick
Betreten verboten! Kinder haften für ihre Eltern!
 

CO2

ehemals "SirMO"

BeitragSo, Feb 24, 2013 18:34
Antworten mit Zitat
Benutzer-Profile anzeigen
Habe ich zwar schon, aber warum nicht:

Folgende Beispielsituation: In der main-Funktion eines C-Programms wird zunächst eine Liste erstellt. Nun füge ich 5 Elemente hinzu, welche ich mit den Indexen 1 - 5 ansprechen kann. Mal Angenommen, es gibt keinen expliziten Index, sondern dieser wird "On-The-Run" Berechnet, je nach Anzahl der Schritte: Wenn es nun beispielsweise eine 2. Funktion gibt, welche der Liste weitere 5 Elemente vorn anfügt und diese in der Main-Funktion aufgerufen wird, so hat das Element, welches ganz am Anfang des Programms noch den Index 4 bzw. 5 hatte nun den Index 9 bzw. 10. Fügt man dann weitere Elemente der Liste vorn oder mittig an, so verändert sich der Index jedes Mal und der Programmierer muss zu jeder Zeit wissen, mit welchem Index er welches Element in der Liste anspricht.
Daher habe ich mich für einen festen Index entschieden: Hiermit ist es egal ob und wo Elemente hinzugefügt werden, die alten Elemente haben immer den selben Index (Sie können maximal durch die Funktion ListChangeIndex() geändert werden).

Das ist meine Begründung für den Nutzen eines Index.
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti

BladeRunner

Moderator

BeitragSo, Feb 24, 2013 21:14
Antworten mit Zitat
Benutzer-Profile anzeigen
Hauptanwendungsgebiet einer Liste sind eben Sammlungen von Objekten bei denen man nicht weiss (bzw. exakt wissen muss) wieviele denn gerade vorhanden sind. Wenn Du in deinem Code anhand fixer ID auf die Elemente der Liste zugreifst wird die Liste als solches obsolet, da ja ein Link zu jedem Objekt schon in irgendeiner Form im Code hardcodiert sein muss. Daher kann ich deiner Argumentation für die Indexierung nicht wirklich folgen.
Wenn ich eine Struktur brauche bei der ich anhand eines festen Index zugreifen muss wäre für mich eine Map oder ein Array Mittel der Wahl.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92
 

CO2

ehemals "SirMO"

BeitragMo, Feb 25, 2013 15:58
Antworten mit Zitat
Benutzer-Profile anzeigen
Nur damit wir uns richtig verstehen: Einen Index gibt es so wie so, da man anders nicht an die Elemente in der Liste herankommt. Ich weiß, dass es bei vielen Listen so ist, dass wie schon gesagt der Index berechnet wird und nicht Extra im Element gespeichert wird. Allerdings finde ich es so nicht so schön, da der Programmierer zu jeder Zeit wissen muss, mit welchem Index er welche Ressource anspricht. Gibt er am Anfang allerdings einen Index vor, kann dieser nicht geändert werden, egal ob "davor" oder "danach" Elemente hinzugefügt / gelöscht werden.

Zitat:
(...) wäre für mich eine Map oder ein Array Mittel der Wahl.

- Array: Dieses müsste jedoch mit einem Wert von Feldern fix initialisiert werden, die gespeichert werden können, gibt es weniger zu speichern, so ist es "Platzverschwendung", gibt es mehr: Pech gehabt. Und das ist meiner Meinung nach der Vorteil einer Liste: Man muss sich vorher nicht festlegen, wie viele Elemente maximal Platz haben, man kann je nach Belieben Elemente entfernen / hinzufügen.
mfG, CO²

Sprachen: BlitzMax, C, C++, C#, Java
Hardware: Windows 7 Ultimate 64-Bit, AMX FX-6350 (6x3,9 GHz), 32 GB RAM, Nvidia GeForce GTX 750 Ti

BladeRunner

Moderator

BeitragMo, Feb 25, 2013 16:21
Antworten mit Zitat
Benutzer-Profile anzeigen
Wieso muss der Programmierer den Index in einer Liste kennen?
Eine verkettete Liste zeichnet sich doch grade dadurch aus dass sie ohne Index auskommt. Du rufst next() auf und kriegst das nächste Element, Fertig. Sch... egal an welcher Stelle es steht.
Das Array erwähnte ich wegen des Indexes, es ist für starre Daten ideal. Die Map ist dann für unbekannte Mengen an Daten perfekt, auf die über feststehende Indizes zugegriffen werden soll.
Ab davon kann man auch ein Array intelligent vergrößern und verkleinern, da bietet sich ein verdoppeln bei erreichen der Lestungsgrenze an. Durch das exponentielle Wachstum wird es nur sehr selten zu Manipulationen kommen.
Aber eine Liste ist gekennzeichnet durch den sequentiellen Zugriff.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

Noobody

BeitragDi, Feb 26, 2013 20:00
Antworten mit Zitat
Benutzer-Profile anzeigen
So eine kleine Library ist ja zu Lernzwecken ganz nützlich, aber damit sie für echte Anwendungsfälle auch nützlich ist, sind da noch ein paar Dinge nicht ok.

1. Für C++ ist der Code nicht sinnvoll, da die STL bereits eine Datenstruktur list enthält, die mit zum einen ein OO Interface anbietet und zum anderen Template-basiert ist, welches es sehr viel angenehmer in der Anwendung macht. Unter anderem bietet es auch mehr Features und integriert nahtlos mit dem Rest der STL (z.B. alle Funktionen aus algorithm, u.a. sortieren).
Prinzipiell ist es für C++ nicht sinnvoll, solche unspezifische Datenstrukturen neu zu implementieren, da es sie alle bereits in der Standard-Library gibt und sie für den durchschnittlichen Anwendungsfall meistens durchdachter designt und implementiert, als man das selber tun könnte.

2. Für C ist der Code schon mal keine gute Idee, weil er allgemein akzeptierte Code-Standards (Englisch, underscore_case, Speicherallozierung in Unterfunktionen vermeiden etc.) ignoriert. Der Code ist ausserdem schon mal sehr umständlich in der Anwendung, weil man die ganze Zeit zu void* hin- und zurückcasten muss, was nicht mal typesafe ist, da man zum einen keine Garantie betreffend der Grösse eines void* bekommt (und darum nicht weiss, ob ein int darin Platz hat) und zum anderen nicht-integrale Datentypen wie floats oder structs natürlich nicht darin unterbringen kann und darum jedes einzelne Element neu allozieren muss.
Der Featureumfang ist auch ziemlich mager mit Element Anhängen, Löschen, Zählen und zwei fragwürdigen Index-Funktionen.

Wenn du deinen Code verbessern willst, empfehle ich einen Blick auf den ut-list Header, der eine hervorragende Implementation einer Liste für C hat. Anstatt von Funktionen hat man generische Makros, die auf beliebigen structs mit next und prev Membern, unabhängig vom Datentyp, arbeiten können, einem die Speicherallozierung selber überlassen und in den nur 700 Zeilen sämtliche Listenfunktionen (Append, Delete, Concatenate, Sort, Iterate etc.) für singly-, doubly- und circular doubly linked lists anbieten.

Aber wie gesagt, eigentlich lohnt es sich in C meistens nicht (und in C++ erst recht nicht), Datenstrukturen wie die Liste neu zu implementieren, da man das grundsätzlich als "gelöstes" Problem betrachten darf.
Man is the best computer we can put aboard a spacecraft ... and the only one that can be mass produced with unskilled labor. -- Wernher von Braun

Neue Antwort erstellen


Übersicht Andere Programmiersprachen Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group