Finden Sie in linearer Zeit heraus, ob sich in einem sortierten Vektor ein Paar befindet, das sich zu einem bestimmten Wert addiert

8

Angesichts eines std::vectorbestimmten Elements, das in aufsteigender Reihenfolge sortiert ist, möchte ich einen Algorithmus entwickeln, der bestimmt, ob die Sammlung zwei Elemente enthält, deren Summe ein bestimmter Wert ist sum.

Ich habe zwei verschiedene Ansätze mit ihren jeweiligen Kompromissen ausprobiert:

  1. Ich kann den gesamten Vektor scannen und für jedes Element im Vektor die binäre Suche ( std::lower_bound) auf den Vektor anwenden, um ein Element zu suchen, das der Differenz zwischen sumund dem aktuellen Element entspricht. Dies ist eine O (n log n) -Zeitlösung, die keinen zusätzlichen Speicherplatz benötigt.

  2. Ich kann den gesamten Vektor durchlaufen und einen füllen std::unordered_set. Dann scanne ich den Vektor und suche für jedes Element im std::unordered_setnach dem Unterschied zwischen sumund dem aktuellen Element. Da die Suche in einer Hash-Tabelle im Durchschnitt in konstanter Zeit ausgeführt wird, wird diese Lösung in linearer Zeit ausgeführt. Diese Lösung erfordert jedoch aufgrund der std::unordered_setDatenstruktur zusätzlichen linearen Raum .

Trotzdem suche ich nach einer Lösung, die in linearer Zeit läuft und keinen zusätzlichen linearen Raum benötigt. Irgendwelche Ideen? Es scheint, dass ich gezwungen bin, Geschwindigkeit gegen Weltraum zu tauschen.

Lui
quelle

Antworten:

10

Da das std::vectorbereits sortiert ist und Sie die Summe eines Paares im laufenden Betrieb berechnen können , können Sie mit O (1) -Raum eine lineare Zeitlösung in der Größe des Vektors erzielen.

Das Folgende ist eine STL-ähnliche Implementierung, die keinen zusätzlichen Speicherplatz benötigt und in linearer Zeit ausgeführt wird:

template<typename BidirIt, typename T>
bool has_pair_sum(BidirIt first, BidirIt last, T sum) {
    if (first == last)
        return false; // empty range

   for (--last; first != last;) {
      if ((*first + *last) == sum)
         return true; // pair found

      if ((*first + *last) > sum)
         --last; // decrease pair sum
      else // (*first + *last) < sum (trichotomy)
         ++first; // increase pair sum
   }

    return false;
}

Die Idee ist, den Vektor von beiden Enden - vorne und hinten - gleichzeitig in entgegengesetzte Richtungen zu durchlaufen und dabei die Summe des Elementpaares zu berechnen.

Ganz am Anfang besteht das Paar aus den Elementen mit den niedrigsten bzw. höchsten Werten. Wenn die resultierende Summe niedriger als ist sum, fahren Sie fort first- der Iterator zeigt auf das linke Ende. Andernfalls bewegen Sie sich last- der Iterator zeigt auf das rechte Ende - rückwärts. Auf diese Weise nähert sich die resultierende Summe zunehmend an sum. Wenn beide Iteratoren auf dasselbe Element zeigen und kein Paar gefunden wurde, dessen Summe gleich sumist, gibt es kein solches Paar.

auto main() -> int {
   std::vector<int> vec{1, 3, 4, 7, 11, 13, 17};

   std::cout << has_pair_sum(vec.begin(), vec.end(), 2) << ' ';
   std::cout << has_pair_sum(vec.begin(), vec.end(), 7) << ' ';
   std::cout << has_pair_sum(vec.begin(), vec.end(), 19) << ' ';
   std::cout << has_pair_sum(vec.begin(), vec.end(), 30) << '\n';
}

Die Ausgabe ist:

0 1 0 1

Dank der generischen Natur der Funktionsvorlage has_pair_sum()und da nur bidirektionale Iteratoren erforderlich sind, funktioniert diese Lösung auch mit std::list:

std::list<int> lst{1, 3, 4, 7, 11, 13, 17};
has_pair_sum(lst.begin(), lst.end(), 2);
眠 り ネ ロ ク
quelle
5

Ich hatte die gleiche Idee wie in der Antwort von 眠 り ネ ロ ク , aber mit einer etwas verständlicheren Implementierung.

bool has_pair_sum(std::vector<int> v, int sum){
    if(v.empty())
        return false;

    std::vector<int>::iterator p1 = v.begin();
    std::vector<int>::iterator p2 = v.end(); // points to the End(Null-terminator), after the last element
    p2--; // Now it points to the last element.

    while(p1 != p2){  
        if(*p1 + *p2 == sum)
            return true;
        else if(*p1 + *p2 < sum){ 
            p1++;
        }else{
            p2--;
        }
    }

    return false;
}
Atr0x
quelle
Vielen Dank für Ihre Kommentare. Ich habe jetzt den Beitrag bearbeitet.
Atr0x
2

Nun, da wir bereits ein sortiertes Array erhalten, können wir dies mit zwei Zeigern tun. Wir behalten zuerst einen linken Zeiger am Anfang des Arrays und einen rechten Zeiger am Ende des Arrays und prüfen dann in jeder Iteration, ob die Summe des Wertes von linker Zeigerindex und Wert des rechten Zeigerindex sind gleich oder nicht, wenn ja, kehren Sie von hier zurück, andernfalls müssen wir entscheiden, wie die Grenze verringert werden soll, dh entweder den linken Zeiger erhöhen oder den rechten Zeiger verringern, also vergleichen wir die temporäre Summe mit gegebene Summe und wenn diese temporäre Summe größer als die gegebene Summe ist, dann entscheiden wir uns, den rechten Zeiger zu reduzieren. Wenn wir den linken Zeiger erhöhen, bleibt die temporäre Summe gleich oder erhöht sich nur, aber niemals kleiner, also beschließen wir, den rechten Zeiger so zu reduzieren vorübergehende Summenabnahme und wir erreichen nahe unserer gegebenen Summe, ähnlich, wenn die vorübergehende Summe kleiner als die gegebene Summe ist,Daher bleibt keine Bedeutung des Reduzierens des rechten Zeigers als temporäre Summe entweder Summe oder verringert sich mehr, erhöht sich jedoch nie, sodass wir unseren linken Zeiger erhöhen, sodass sich unsere temporäre Summe erhöht und wir nahe an der angegebenen Summe erreichen, und wir führen den gleichen Vorgang immer wieder durch, es sei denn, wir Holen Sie sich die gleiche Summe oder der Wert des linken Zeigerindex wird größer als der rechte Zeigerindex des rechten Zeigers oder umgekehrt. Dies ist der Code zur Demonstration. Lassen Sie mich wissen, wenn etwas nicht klar istLassen Sie mich wissen, wenn etwas nicht klar istLassen Sie mich wissen, wenn etwas nicht klar ist

bool pairSumExists(vector<int> &a, int &sum){
    if(a.empty())
    return false;

    int len = a.size();
    int left_pointer = 0  , right_pointer = len - 1;

    while(left_pointer < right_pointer){
        if(a[left_pointer] + a[right_pointer] == sum){
            return true;
        }
        if(a[left_pointer] + a[right_pointer] > sum){
            --right_pointer;
        }
        else
        if(a[left_pointer] + a[right_poitner] < sum){
            ++left_pointer;
        }
    }
    return false;
}
mss
quelle