Prioritätswarteschlange (priority_queue), Funktor (Funktionsobjekt) in C++

Inhaltsverzeichnis

Die Verwendung von „priority_queue“.

Funktor (Funktionsobjekt)

Häufige klassische OJ-Fragen in Prioritätswarteschlangen (häufig in Interviews getestet)


Eine Prioritätswarteschlange ist ein Containeradapter, dessen erstes Element gemäß strengen schwachen Sortierkriterien immer das größte seiner enthaltenen Elemente ist. Eine Prioritätswarteschlange ist eine Sammlung von 0 oder mehr Elementen, und jedes Element hat eine Priorität oder einen Wert. Die in der Prioritätswarteschlange ausgeführten Operationen sind 1) ein Element finden; 2) ein neues Element einfügen; 3) ein Element löschen. Die diesen Operationen entsprechenden Funktionen sind „Top“, „Push“ und „Pop“. In der Warteschlange mit minimaler Priorität (Min Priority Queue) sind die zu suchenden und zu löschenden Elemente die Elemente mit der niedrigsten Priorität; in der Warteschlange mit maximaler Priorität (Max Priority Queue) sind die zu suchenden und zu löschenden Elemente die Elemente mit der höchsten Priorität. Die Elemente der Prioritätswarteschlange können die gleiche Priorität haben und für solche Elemente können Suche und Löschung in beliebiger Reihenfolge durchgeführt werden. Eine Prioritätswarteschlange ähnelt einem Heap, bei dem Elemente jederzeit eingefügt werden können und nur das größte Heap-Element (das in der Prioritätswarteschlange ganz oben) abgerufen werden kann . Die Prioritätswarteschlange wird als Containeradapter implementiert, der eine bestimmte Containerklasse als zugrunde liegende Containerklasse kapselt, und die Warteschlange stellt eine Reihe spezifischer Mitgliedsfunktionen für den Zugriff auf ihre Elemente bereit. Elemente werden aus dem „Ende“ eines bestimmten Containers entnommen, der als oberster Punkt der Prioritätswarteschlange bezeichnet wird.

Die Verwendung von „priority_queue“.

Die Prioritätswarteschlange verwendet standardmäßig den Vektor als zugrunde liegenden Datenspeichercontainer, und der Heap-Algorithmus wird für den Vektor verwendet, um die Elemente im Vektor in eine Heap-Struktur zu konstruieren. Priority_queue ist also ein Heap, und Sie können die Verwendung von Priority_queue an jedem beliebigen Ort in Betracht ziehen um den Heap zu verwenden.

Hinweis: Priority_queue ist standardmäßig ein großer Heap.

Funktionsdeklaration Schnittstellenbeschreibung
priority_queue()/priority_queue(first,last)  Erstellt eine leere Prioritätswarteschlange
leer() Überprüfen Sie, ob die Prioritätswarteschlange leer ist, geben Sie true zurück, andernfalls geben Sie false zurück
Spitze() Gibt das größte (kleinste Element) in der Prioritätswarteschlange zurück, also das oberste Element des Heaps
push(x) Fügen Sie Element x in die Prioritätswarteschlange ein
Pop() Löschen Sie das größte (kleinste) Element in der Prioritätswarteschlange, dh das oberste Element des Heaps

1. Priority_queue ist standardmäßig ein großer Stapel. 

 2. Wenn Sie Daten eines benutzerdefinierten Typs in die Prioritätswarteschlange einfügen, muss der Benutzer >- oder <-Überladungen im benutzerdefinierten Typ bereitstellen.

Klasse Date
{ public:     Date(int Jahr = 1900, int Monat = 1, int Tag = 1)         : _year(year)         , _month(month)         , _day(day)     {}     bool Operator<(const Date& d)const     {         return (_Jahr < d._Jahr) ||             (_year == d._year && _month < d._month) ||             (_year == d._year && _month == d._month && _day < d._day);     }     bool Operator>(const Date& d)const     {         return (_year > d._year) ||             (_year == d._year && _month > d._month) ||             (_year == d._year && _month == d. _Monat && _Tag > d._Tag);     }


















größer<Datum>> q2;     q2.push(Datum(2023, 3, 29));


















  

    q2.push(Datum(2023, 3, 28));
    q2.push(Datum(2023, 3, 30));
    cout << q2.top() << endl;

Funktor (Funktionsobjekt)

Definition von Functor
Functor (Functor), auch bekannt als Function Object (Function Object), ist eine Klasse, die Funktionsfunktionen ausführen kann. Die Syntax des Funktors ist fast die gleiche wie bei unserem gewöhnlichen Funktionsaufruf, aber als Klasse des Funktors muss der Operator „operator()“ überladen werden. Weil der Aufruf des Funktors tatsächlich den überladenen Operator () über das Klassenobjekt aufruft.

Wenn der Programmierer eine bestimmte „Operation“ als Parameter des Algorithmus verwenden möchte, gibt es im Allgemeinen zwei Methoden:
(1) Eine Möglichkeit besteht darin, die „Operation“ zuerst als Funktion zu entwerfen und dann den Funktionszeiger als Parameter zu verwenden des Algorithmus.
(2) Entwerfen Sie die „Operation“ als Funktor (eine Klasse auf Sprachebene), generieren Sie dann mit dem Funktor ein Objekt und verwenden Sie dieses Objekt als Parameter des Algorithmus.

Offensichtlich ist die zweite Methode besser, da die erste Methode weniger skalierbar ist und bei Änderungen der Funktionsparameter nicht mit dem alten Code kompatibel sein kann. Wenn wir Code schreiben, stellen wir manchmal fest, dass einige Funktionscodes kontinuierlich verwendet werden. Um den Code wiederzuverwenden, ist die Implementierung als öffentliche Funktion eine Problemumgehung. Einige von der Funktion verwendete Variablen können jedoch öffentliche globale Variablen sein. Die Einführung globaler Variablen ist anfällig für gleichnamige Konflikte und unpraktisch in der Wartung.

Verwenden Sie den Funktor, schreiben Sie eine einfache Klasse. Zusätzlich zur Aufrechterhaltung der grundlegenden Mitgliedsfunktionen der Klasse müssen Sie nur den Operator () überladen. Auf diese Weise kann die Wartung einiger öffentlicher Variablen vermieden und der wiederverwendete Code für die nächste Wiederverwendung getrennt werden. Darüber hinaus können Funktoren im Vergleich zu den besseren Eigenschaften von Funktionen auch Abhängigkeiten, Kombinationen und Vererbungen durchführen, was der Ressourcenverwaltung zuträglich ist.

  In der Prioritätswarteschlange ist der Standardwert der große Root-Heap. Wenn wir einen kleinen Root-Heap benötigen, können wir den Funktor verwenden. In C++ müssen Funktoren im Allgemeinen nicht selbst geschrieben werden, es gibt Bibliotheksfunktionen. Die Header-Datei #include <Functional> muss eingebunden werden.

int findKthLargest(vector<int>& nums, int k) {
        //优先级队列默认大堆,使用仿函数建立小堆
        priority_queue<int,vector<int>,greater<int>> pq(nums.begin(),nums.begin()+k);
        for(int i=k;i<nums.size();i++)
        {
            if(nums[i]>pq.top())
            {
                pq.pop();
                pq.push(nums[i]);
            }
        }
        return pq.top();
    }

 Spezielle Funktionen müssen selbst implementiert werden, z. B. der Adressvergleich.

Klasse PDateLess
{
öffentlich:
    bool Operator()(const Date* p1, const Date* p2)
    {
        return *p1 < *p2;
    }
};

Klasse PDateGreater
{
öffentlich:
    bool Operator()(const Date* p1, const Date* p2)
    {
        return *p1 > *p2;
    }
};

int main()
{     Priority_queue<Date*, vector<Date*>, PDateGreater> q;     q.push(neues Datum(2023, 10, 29));     q.push(neues Datum(2023, 10, 30));     q.push(neues Datum(2023, 10, 28));     cout << *(q2.top()) << endl; }





Häufige klassische OJ-Fragen in Prioritätswarteschlangen (häufig in Interviews getestet)

 Verweis auf Angebot 41. Median in Datenströmen

Wie erhalte ich den Median in einem Datenstrom? Wenn eine ungerade Anzahl an Werten aus dem Datenstrom gelesen wird, ist der Median der Wert in der Mitte aller sortierten Werte. Wenn eine gerade Anzahl von Werten aus dem Datenstrom gelesen wird, ist der Median der Durchschnitt der beiden mittleren Zahlen, nachdem alle Werte sortiert wurden.

Zum Beispiel,

[2,3,4] hat einen Median von 3

Der Median von [2,3] beträgt (2 + 3) / 2 = 2,5

Entwerfen Sie eine Datenstruktur, die die folgenden zwei Operationen unterstützt:

  • void addNum(int num) – Fügt eine Ganzzahl aus dem Datenstrom zur Datenstruktur hinzu.
  • double findMedian() – Gibt den Median aller Elemente bisher zurück.

Der große obere Heap speichert Zahlen, die nicht größer als der Median sind, d. h. er speichert die linke Hälfte des geordneten Arrays. Der
kleine obere Heap speichert Zahlen, die größer als der Median sind, d. h. er speichert die rechte Hälfte des geordneten Arrays. Halber Teil
Die Oberseite des großen oberen Heaps stellt die größte Zahl in der linken Hälfte dar, und
die Oberseite des kleinen oberen Heaps stellt die kleinste Zahl in der rechten Hälfte dar.
Gleichzeitig ist die Differenz zwischen der Zahl Die Anzahl der in den beiden Heaps gespeicherten Elemente wird 1 nicht überschreiten.
Wenn die Gesamtzahl der gespeicherten Elemente eine ungerade Zahl ist, ist die Oberseite des Big-Top-Heaps (d. h. die linke Hälfte des Heaps) der Median. Wenn die
Summe Die in den beiden Heaps gespeicherte Anzahl ist gerade, die mittlere
Verbindung kann durch Kombinieren der Oberseite des Big-Top-Stapels und der Oberseite des Small-Top-Stapels erhalten werden. Betrachten wir das Einfügeproblem, das heißt, wie Daten eingefügt werden, wenn neue Elemente eingehen In? 
Wenn die Datenmenge in den beiden Heaps gleichmäßig ist, fügen Sie sie zuerst in den kleinen oberen Heap ein, um den Mindestwert zu ermitteln (oberes Heap-Element), und fügen Sie dann den Mindestwert zu diesem Zeitpunkt
in den großen oberen Heap (linke Hälfte) ein Die Datenmenge der beiden Heaps ist ungerade, und der Median ist die Spitze des Big-Top-Heaps
. Wenn die Datenmenge in den beiden Heaps ungerade ist, fügen Sie sie zuerst in den Big-Top-Heap ein, um den Maximalwert (Heap) zu ermitteln oberstes Element) und
geben Sie dann den Maximalwert in „Gehe zum kleinen oberen Heap“ (rechte Hälfte) ein. Zu diesem Zeitpunkt ist die Datenmenge in den beiden Heaps gerade und der Median wird aus der Oberseite der beiden Heaps berechnet .

class MedianFinder {
public:
    // 最大堆,存储左边一半的数据,堆顶为最大值
    priority_queue<int, vector<int>, less<int>> maxHeap;
    // 最小堆, 存储右边一半的数据,堆顶为最小值
    priority_queue<int, vector<int>, greater<int>> minHeap;
    /** initialize your data structure here. */
    MedianFinder() {
    }

    // 维持堆数据平衡,并保证左边堆的最大值小于或等于右边堆的最小值
    void addNum(int num) {
        /*
         * 当两堆的数据个数相等时候,左边堆添加元素。
         * 采用的方法不是直接将数据插入左边堆,而是将数据先插入右边堆,算法调整后
         * 将堆顶的数据插入到左边堆,这样保证左边堆插入的元素始终是右边堆的最小值。
         * 同理左边数据多,往右边堆添加数据的时候,先将数据放入左边堆,选出最大值放到右边堆中。
         */
        if (maxHeap.size() == minHeap.size()) {
            minHeap.push(num);
            int top = minHeap.top();
            minHeap.pop();
            maxHeap.push(top);
        } else {
            maxHeap.push(num);
            int top = maxHeap.top();
            maxHeap.pop();
            minHeap.push(top);
        }
    }
    
    double findMedian() {
        if (maxHeap.size() == minHeap.size()) {
            return (maxHeap.top()+minHeap.top())*1.0/2;
        } else {
            return maxHeap.top()*1.0;
        }
    }
};

 Ordnen Sie das Array in der kleinsten Zahl an

Geben Sie ein Array nicht negativer Ganzzahlen ein, verketten Sie alle Zahlen im Array, um eine Zahl zu bilden, und geben Sie die kleinste aller Zahlen aus, die verkettet werden können.

Bei dieser Frage wird nach der kleinsten Zahl gesucht, die zusammengefügt werden kann, was im Wesentlichen ein Sortierproblem darstellt. Seien die Zeichenfolgen zweier beliebiger Zahlen im Array nums x und y, dann wird die Sortierbeurteilungsregel wie folgt festgelegt:

Wenn die verkettete Zeichenfolge x+y>y+x ist, dann ist x „größer als“ y;
andernfalls, wenn x+y<y+x, dann ist x „kleiner als“ y; x
„kleiner als“ y bedeutet: Nachher Die Sortierung ist abgeschlossen, im Array sollte x links von y stehen; „größer als“ umgekehrt.

Klasse Lösung {

öffentlich:

    // Funktor

    bool-Operator()(String s1,String s2)

    {

        return s1+s2 < s2+s1;

    }

    string minNumber(vector<int>& nums)

    {

        vector<string> a;

        for(int i=0;i<nums.size();i++)

        {

            a.push_back(to_string(nums[i]));

        }

        sort(a.begin(),a.end(),Solution());//Verwendung des Funktors

        string ret;

        for(int i=0;i<a.size();i++)

        {

            ret += a[i];

        }

        return ret;

    }

};

Supongo que te gusta

Origin blog.csdn.net/m0_55752775/article/details/129860166
Recomendado
Clasificación