Maximum Sum Subarray of Size K
Du erhältst ein Array aus Ganzzahlen nums und eine Fensterlänge k. Betrachte jede Folge von genau k benachbarten Elementen und gib die größte Summe darunter zurück. Die Werte können negativ sein, daher kann auch die Antwort negativ sein.
Funktion
- numsinteger-array
- das Array aus Ganzzahlen
- kinteger
- wie viele benachbarte Elemente jedes Fenster enthält
- Gibt zurückinteger
- die größte Summe von k aufeinanderfolgenden Elementen
Einschränkungen
1 ≤ k ≤ nums.length ≤ 104-104 ≤ nums[i] ≤ 104
Beispiele
- Eingabe
- nums = [4, -1, 3, 7, -2, 5, 1]k = 3
- Ausgabe
- 10
- Erklärung
- Die fünf Fenster der Länge 3 ergeben zusammen
6,9,8,10und4. Das größte ist7 + (-2) + 5 = 10.
- Eingabe
- nums = [-3, -8, -1, -6]k = 2
- Ausgabe
- -7
- Erklärung
- Jeder Wert ist negativ, also ist auch jede Fenstersumme negativ:
-11,-9und-7. Die größte davon ist-1 + (-6) = -7.
- Eingabe
- nums = [5, -2, 4]k = 3
- Ausgabe
- 7
- Erklärung
- Wenn
kder Länge des Arrays entspricht, gibt es ein Fenster: das ganze Array, und5 + (-2) + 4 = 7.
+15 versteckte Tests beim Einreichen
Weiterführende Frage
Kannst du auch zurückgeben, wo das beste Fenster beginnt, und bei Gleichstand das am weitesten links liegende auswählen?
Tipps
Öffne sie nacheinander. Jeder verrät ein bisschen mehr.
Schreibe die Summen zweier benachbarter Fenster auf, zum Beispiel die des Fensters, das bei Index 0 beginnt, und die des Fensters, das bei Index 1 beginnt. Was haben sie gemeinsam?
Sie haben
k-1Elemente gemeinsam. Wenn das Fenster um einen Schritt nach rechts verschoben wird, kommt ein neues Element hinzu und ein altes wird entfernt, sodass die neue Summe in zwei Operationen aus der alten berechnet werden kann.Addiere die ersten
kElemente einmal. Addiere dann für jedesivonkbis zum Endenums[i], ziehenums[i-k]ab und behalte die größte bisher gefundene Summe bei.
Lösung
Es gibt n-k+1 Fenster, und jedes von Grund auf zu summieren, erfordert k Additionen. Der Trick besteht darin, dass sich zwei benachbarte Fenster bis auf zwei Elemente überschneiden. Verschiebe das Fenster, statt es neu aufzubauen: Ein Wert kommt hinzu, ein Wert fällt weg, und jede Fenstersumme erfordert zwei Operationen.
Alle Fenster addieren
Korrekt, wird aber bei den größten Tests nicht fertig
Idee
Ein Fenster wird dadurch festgelegt, wo es beginnt. Es kann am Index 0, 1 und so weiter bis n-k beginnen, da ein späterer Start über das Ende des Arrays hinausgehen würde. Addiere für jeden Start die k Elemente und vergleiche die Summe mit dem bisher besten Wert.
Für [4, -1, 3, 7, -2, 5, 1] und k = 3 ergeben sich die Summen 6, 9, 8, 10, 4, und die Antwort ist 10. Setze den besten Wert auf die Summe des ersten Fensters oder auf die kleinste ganze Zahl, niemals auf 0: Wenn alle Werte negativ sind, wäre 0 größer als jedes tatsächliche Fenster.
Der Aufwand beträgt (n-k+1) × k Additionen. Er ist am größten, wenn k etwa der Hälfte von n entspricht: Bei n = 10^4 und k = 5000 sind das 5001 × 5000, also etwa 2.5 × 10^7 Additionen, von denen fast alle Arbeit wiederholen, die bereits für das vorherige Fenster erledigt wurde.
Algorithmus
- Setze
bestauf den kleinstmöglichen Wert. - Setze für jeden Startwert von
0bisn-ktotal = 0. - Addiere
nums[start]bisnums[start+k-1]zutotal. - Wenn
totalgrößer alsbestist, speichere es. - Gib
bestzurück.
def maxSumSubarray(nums, k):
n = len(nums)
best = None
for start in range(n - k + 1):
total = 0
for i in range(start, start + k):
total += nums[i]
if best is None or total > best:
best = total
return bestEin festes Fenster verschieben
Idee
Vergleiche das Fenster, das bei Index 0 beginnt, mit dem, das bei Index 1 beginnt. In [4, -1, 3, 7, -2, 5, 1] mit k = 3 sind das 4 + (-1) + 3 = 6 und (-1) + 3 + 7 = 9. Beide enthalten -1 und 3. Die zweite Summe ist die erste plus dem hinzugekommenen Wert 7 minus dem herausgefallenen Wert 4: 6 + 7 - 4 = 9.
Das gilt für jeden Schritt. Wenn sich das rechte Ende des Fensters zu Index i bewegt, kommt das Element bei i hinzu und das Element bei i-k fällt heraus. Du berechnest also zuerst einmal die Summe des ersten Fensters und aktualisierst dann die Summe bei jedem Schritt mit einer Addition und einer Subtraktion. Die Summen lauten 6, 9, 8, 10, 4, genau wie bei der Brute-Force-Methode, und du behältst den größten Wert.
Jedes Element kommt einmal hinzu und fällt höchstens einmal heraus, daher beträgt die Laufzeit O(n). Du speicherst zwei Zahlen, die aktuelle Fenstersumme und die beste Summe, daher beträgt der zusätzliche Speicherplatz O(1). Keine Summe hier überschreitet 10^4 × 10^4 = 10^8, daher reicht eine 32-Bit-Ganzzahl aus.
Algorithmus
- Addiere
nums[0]bisnums[k-1]zuwindow. - Setze
best = window. - Addiere für jedes
ivonkbisn-1nums[i]und ziehenums[i-k]ab. - Setze nach jedem Schritt
bestauf den größeren Wert vonbestundwindow. - Gib
bestzurück.
def maxSumSubarray(nums, k):
# Sum of the first window, nums[0..k-1].
window = sum(nums[:k])
best = window
for i in range(k, len(nums)):
# Slide one step right: nums[i] enters, nums[i-k] leaves.
window += nums[i] - nums[i - k]
best = max(best, window)
return best
Stolperfallen und Grenzfälle
Die Idee mit dem Fenster ist einfach, daher verstecken sich die Fehler in den Startwerten und den Indizes.
bestmit0zu initialisieren. Bei[-3, -8, -1, -6]undk = 2ist die tatsächliche Antwort-7, aber einbestvon0wird nie übertroffen und als Antwort zurückgegeben.- Das falsche Element abzuziehen. Wenn
nums[i]hinzukommt, ist das Element, das das Fenster verlässt,nums[i-k]. Mitnums[i-k+1]odernums[i-k-1]erhältst du Fenster mit der falschen Länge. - Die Brute-Force-Suche einen Start zu früh zu beenden. Das letzte Fenster beginnt bei
n-k, daher muss die Schleife diesen Wert einschließen. Beik = nist das das einzige Fenster; ein Off-by-one-Fehler prüft kein Fenster und gibt den Startwert vonbestzurück. - Erst nach der Schleife zu vergleichen. Das beste Fenster kann das erste sein, also vergleiche auch die erste Summe oder initialisiere
bestmit ihr. - Zu vergessen, dass R und Lua ab 1 zählen. Das erste Fenster ist
nums[1..k], und das Element, das das Fenster verlässt, wennnums[i]hinzukommt, ist weiterhinnums[i-k].
Häufige Fragen4
Was ist ein Fenster fester Größe?
Es ist ein Bereich aus genau k benachbarten Elementen, der sich Schritt für Schritt über ein Array bewegt. Statt den Bereich an jeder Position von Grund auf neu zu berechnen, aktualisierst du einen laufenden Wert: Addiere das Element, das rechts hinzukommt, und entferne das Element, das links wegfällt. Dadurch wird der Aufwand von O(n·k) auf O(n) reduziert.
Wie hoch ist die Zeitkomplexität des Teilarrays der Größe k mit der maximalen Summe?
Mit einem gleitenden Fenster beträgt der Zeitaufwand O(n) und der zusätzliche Speicherplatz O(1): ein Durchlauf, um die Summe des ersten Fensters zu berechnen, danach pro Schritt eine Addition und eine Subtraktion. Jedes Fenster separat zu summieren kostet (n-k+1) × k Additionen, also O(n·k), bei n = 10^4 und k = 5000 etwa 2.5 × 10^7.
Worin unterscheidet sich das vom Maximum-Subarray-Problem?
Hier ist die Länge auf k festgelegt, daher ist jeder Kandidat ein Fenster, und eine gleitende Summe erfasst sie alle. Beim Problem des maximalen Teilarrays ist die Länge frei wählbar, und du benötigst Kadane's Algorithmus, der bei jedem Element entscheidet, ob der aktuelle Abschnitt verlängert oder ein neuer begonnen werden soll. Bei einem festen Fenster hast du diese Wahl nie.
Kann man das auch mit Präfixsummen lösen?
Ja. Erstelle prefix[i] als Summe der ersten i Elemente, und das Fenster, das bei s beginnt, ergibt die Summe prefix[s+k] - prefix[s]. Das benötigt ebenfalls O(n) Zeit, speichert aber n+1 Summen. Mit dem gleitenden Fenster erhältst du dieselben Summen mit zwei Variablen.
Ähnliche Aufgaben
Aufgaben mit denselben Ideen. Wer zwei oder drei davon löst, behält das Muster.
Python
def maxSumSubarray(nums, k):
# Schreibe hier den CodeFall 1
Fall 2
Fall 3
Eingabe
nums = [4, -1, 3, 7, -2, 5, 1] k = 3
Erwartet
10