Bestimmter Wert schon im vector enthalten?



  • Hallo zusammen,

    kann man irgendwie prüfen, ob ein bestimmter Wert schon in einem vector enthalten ist oder muss ich mir das selbst schreiben?

    Gruß
    Patrick



  • Nimm einfach find() dafür 😉



  • Wenn du öfter solche Abfragen machst, ist ein sort und binary_search angebracht.



  • Danke erstmal.

    @CStoll: Was muss ich einbinden um find() nutzen zu können?

    @Ponto: Hast Du vielleicht noch ein wenig mehr Input für mich?

    Gruß
    Patrick



  • PACoSys schrieb:

    @Ponto: Hast Du vielleicht noch ein wenig mehr Input für mich?

    Nicht wirklich. Es hängt vom Anwendungsfall ab. Aber generell geht es so:

    Header:

    #include <algorithm>
    

    Sortieren:

    std::sort(vec.begin(), vec.end());
    

    Ist der Wert schon drin?

    if (std::binary_search(vec.begin(), vec.end(), 5)) {
    ... 
    }
    


  • PACoSys schrieb:

    Danke erstmal.

    @CStoll: Was muss ich einbinden um find() nutzen zu können?

    Den Header <algorithm>

    @Ponto: Hast Du vielleicht noch ein wenig mehr Input für mich?

    find() sucht linear, benötigt also (im worst case und im Durchschnitt) eine Laufzeit von O(n). binary_search() verwendet eine binäre Suche und kommt damit auf eine Laufzeit von O(log n) - allerdings erwartet es, daß die Eingabefolge sortiert ist (wenn du die Daten öfter änderst als suchst, lohnt sich das nicht, weil du jedes Mal neu sortieren müsstest).



  • Um heraus zu bekommen, ob ein Wert gefunden wurde, muss ich doch nur mit dem end des vectors vergleichen oder nicht. Komischerweise wird aber kein Eintrag gefunden. In dem vector stehen Pointer auf Point-Objekte und ich habe den == Operator für diese überschrieben (siehe Code). Heißt das im vorliegenden Fall, dass die Koordinaten unterschiedlich sind? Wenn ich sie mir mit cout anzeigen lassen sehe ich nur 4 Nachkommastellen und die sind gleich.

    bool operator == (const Point & crP2) const { return ((x == crP2.GetX()) && (y == crP2.GetY()) && (z == crP2.GetZ())); }
    
    if (find(rPoints.begin(), rPoints.end(), pPoint) == rPoints.end())
    {
    	rPoints.push_back(pPoint);
    }
    

    Gruß
    Patrick



  • Du hast Pointer im vector? Dann werden die auch verglichen und nicht deine Point-Objekte.



  • btw. vielleicht solltest du eher ein std::set als ein std::vector benutzen.



  • Kann man nicht, falls es pointer sind, eine eigene Vergleichsfunktion schreiben, die eben erst dereferenziert und dann vergleicht und diese dann an find übergeben?

    template<class T>
    struct PointerVergleich{
        bool operator()(const T*& l,const T*& r){
            return (*l)==(*r);
    };
    

    so in der Art, bin mir aber recht unsicher, ob ich das jetzt richtig geschrieben hab... bitte korrigieren wenn falsch.

    EDIT: falls du nie gleichen Elemente im Container haben willst, nimm wie gesagt std::set.



  • Shinja schrieb:

    Kann man nicht, falls es pointer sind, eine eigene Vergleichsfunktion schreiben, die eben erst dereferenziert und dann vergleicht und diese dann an find übergeben?

    Operatoren die sich ausschließlich auf eingebaute Typen (und nichts anderes sind Pointer) beziehen lassen sich nicht überladen.



  • Siehe oben, ich meinte nicht überladen vom operator==. Würde es wie oben geschrieben nicht gehen? (den Functor dann halt an find übergeben)



  • find() kannst du keinen zusätzlichen Funktor übergeben. Aber die Lösung geht schon in die richtige Richtung - nimm find_if().



  • Der Funktor stand vorhin noch nicht da 😉
    Ja, das sollte gehen.

    EDIT: Siehe CStoll.



  • Ich weisz, sry, hab den gleich danach hinzugefügt. Aber hier antworten die Leute einfach zu schnell, hehe.

    Achja, find_if() war's, sry. Hatte ich mal wieder vergessen.



  • Erstmal danke für Eure zahlreichen Antworten.

    Habe es so probiert:

    if (find_if(rPoints.begin(), rPoints.end(), ComparePoint(pPoint)) == rPoints.end())
    {
    	//create new point
    }
    

    und

    class ComparePoint
    {
    public:
    	bool operator () (const Point *& crLhs, const Point *& crRhs)
    	{
    		return (*crLhs) == (*crRhs);
    	}
    };
    

    Ich bekomme jetzt beim Compilieren folgenden Fehler: "'<function-style-cast>': 'Point *' kann nicht in 'ComparePoint' konvertiert werden".

    Gruß
    Patrick



  • Ja, Funktoren sind kompliziert 😉 find_if() erwartet eine Funktion/Funktor, die mit einem Parameter aufgerufen werden kann und einen bool zurückgibt - den Vergleich mußt du in deinem op() erledigen:

    struct ComparePoints : public unary_function<Point*,bool>
    {
      //Ctor - legt Vergleichwert fest
      ComparePoints(Point* tgt) : m_tgt(tgt) {}
    
      //op() - vergleicht übergebenen Wert mit Vergleichswert
      bool operator() (Point* akt)
      { return *akt == *m_tgt; }
    private:
      Point* m_tgt;
    };
    
    ...
    if (find_if(rPoints.begin(), rPoints.end(), CompairPoint(pPoint)) == rPoints.end())
    {
        //create new point
    }
    

    (PS: Zeiger sind klein genug, um sie per Value übergeben zu können - da ist die Übergabe als const-Referenz überflüssig ;))



  • Danke funktioniert super. Eine Sache noch. Könnt Ihr Euch folgenden Code mal bitte ansehen und mir schreiben, ob Optimierungen möglich sind (ganz bestimmt sind welche möglich). Außerdem habe ich die Vermutung, dass ich da ein Speicherloch programmiert habe. In Kommentaren gibt es noch eine zweite Variante, die funktioniert aber nicht, da man Iteratoren nicht dereferenzieren kann.

    // Create new point.
    Point * pPoint = new Point(x, y, z);
    
    // If the point does not exist already then create a new one.
    //vector<Point *>::iterator iter = find_if(rPoints.begin(), rPoints.end(), ComparePoint(pPoint)); // version 2
    //if (iter == rPoints.end()) // version 2
    if (find_if(rPoints.begin(), rPoints.end(), ComparePoint(pPoint)) == rPoints.end()) // version 1
    {
    	cout << " not found!!!" << endl;
    	// Point does not exist already.
    	pPoint->SetIndex(rPoints.size());
    
    	rPoints.push_back(pPoint);
    }
    else
    {
    	// Delete pPoint because of memory leak??? But it is needed below again!!!
    	cout << " found." << endl;
    }
    
    // Memorize point index for neighborhoods.
    //int index = (*iter)->GetIndex(); // version 2
    int index = (*find_if(rPoints.begin(), rPoints.end(), ComparePoint(pPoint)))->GetIndex(); // version 1
    

    Gruß
    Patrick



  • Die beste Optimierung: Verwende keine Zeiger, wenn es nicht nötig ist (d.h. wenn keine echten Gründe dagegen sprechen, solltest du du einen vector<Point> verwenden - dann erübrigt sich auch die Speicherveraltung und das Gebastel mit dem Functor).

    Ansonsten: Ja, im else-Zweig solltest du das per new angelegte Objekt wieder freigeben. Und die Suche solltest du nur einmal durchführen und dir das Ergebnis dann merken (die Lösung "version 2" solltest du auf jeden Fall verwenden - im else-Zweig noch ergänzt um ein iter = rPoints.end()-1; ).



  • Variante 2 würde ich ja gerne verwenden, aber Iteratoren können nicht dereferenziert werden.

    Ohne Pointer geht es glaube ich nicht. Mal ein Wenig mehr zum Hintergrund: Ich schreibe mir ein Programm, welches aus einer Datei Flächen einliest. Jede Fläche besteht aus Drahtgittern, diese wiederum aus Kanten und die sind dann durch ihre Anfangs- und Endpunkte beschrieben. Beim Einlesen muss ich mir eine Topologie aufbauen, so dass ich hinterher weiß welche Punkte benachbart sind, welche Punkte zu welchem Drahtgitter gehören und welche Punkte zu einer Fläche gehören. Nachdem ich die Daten eingelesen habe, will ich die Punkte so sortieren, dass die Punkte, die den Rand der Fläche beschreiben in einer bestimmten Reihenfolge sind. Nun hatte ich mir folgendes überlegt:

    Klasse Face:
    - hat einen vector mit allen Punkten => vector<Point *>
    - hat einen vector mit allen Drahtgittern => vector<Wire *>

    Klasse Wire:
    - hat einen vector mit den sortierten Indizes der zugehörigen Punkte => vector<int>

    Klasse Point:
    - Koordinaten => double x, double y und double z
    - Pointer auf Nachbarn (jeweils zwei) => Point * pN0 und Point * pN1

    Beim Einlesen erstelle ich für jede Fläche eine Instanz der Klasse Face, in dieser wird die Methode processFace() aufgerufen. processFace() erstellt für jedes Drahtgitter der Fläche eine Instanz der Klasse Wire, in der dann wiederum processWire() aufgerufen wird. processWire() ruft für jede Kante des Drahtgitters processEdge() auf. processEdge() ist der Kern, hier werden die Instanzen der Point Klasse erstellt, wobei nur ein neuer Punkt erstellt werden soll, wenn nicht schon ein Punkt mit den selben Koordinaten existiert.

    Kann man, dass ohne Pointer umsetzen?

    Gruß
    Patrick



  • Ja, kann man ohne Pointer umsetzen. Solange du ohne Polymorphie auskommst. Und das scheint bei deiner Point und Wire Klasse der Fall zu sein. Probier es doch einfach mal ohne Pointer! (du bist in der C++-Welt!) Der Code wird dadurch sogar kürzer.


Anmelden zum Antworten