81 lines
2.5 KiB
Python
81 lines
2.5 KiB
Python
"""Kadane's algorithm: maximum subarray sum.
|
|
|
|
Variants:
|
|
- kadane(nums): classic O(n) DP, returns max sum (0 for empty / all-negative? no — returns max element for all-negative by default; empty list -> 0).
|
|
- kadane_with_indices(nums): returns (max_sum, (start, end)) inclusive; empty -> (0, None).
|
|
- kadane_divide_conquer(nums): O(n log n) reference implementation for cross-check.
|
|
- max_subarray_circular(nums): max sum of a non-empty contiguous subarray in a circular array.
|
|
"""
|
|
|
|
from typing import List, Optional, Tuple
|
|
|
|
|
|
def kadane(nums: List[int]) -> int:
|
|
if not nums:
|
|
return 0
|
|
best = cur = nums[0]
|
|
for x in nums[1:]:
|
|
cur = x if cur < 0 else cur + x
|
|
# equivalent: cur = max(x, cur + x)
|
|
if cur > best:
|
|
best = cur
|
|
return best
|
|
|
|
|
|
def kadane_with_indices(nums: List[int]) -> Tuple[int, Optional[Tuple[int, int]]]:
|
|
if not nums:
|
|
return 0, None
|
|
best = cur = nums[0]
|
|
best_l = best_r = 0
|
|
cur_l = 0
|
|
for i, x in enumerate(nums[1:], start=1):
|
|
if cur < 0:
|
|
cur = x
|
|
cur_l = i
|
|
else:
|
|
cur += x
|
|
if cur > best:
|
|
best = cur
|
|
best_l = cur_l
|
|
best_r = i
|
|
return best, (best_l, best_r)
|
|
|
|
|
|
def kadane_divide_conquer(nums: List[int]) -> int:
|
|
if not nums:
|
|
return 0
|
|
# returns (total, best_prefix, best_suffix, best_sub)
|
|
def rec(lo, hi):
|
|
if lo == hi:
|
|
v = nums[lo]
|
|
return v, v, v, v
|
|
mid = (lo + hi) // 2
|
|
lt, lp, ls, lb = rec(lo, mid)
|
|
rt, rp, rs, rb = rec(mid + 1, hi)
|
|
total = lt + rt
|
|
prefix = max(lp, lt + rp)
|
|
suffix = max(rs, rt + ls)
|
|
best = max(lb, rb, ls + rp)
|
|
return total, prefix, suffix, best
|
|
return rec(0, len(nums) - 1)[3]
|
|
|
|
|
|
def max_subarray_circular(nums: List[int]) -> int:
|
|
if not nums:
|
|
return 0
|
|
if len(nums) == 1:
|
|
return nums[0]
|
|
# max of (normal kadane) and (total - min_subarray), but not all elements excluded
|
|
total = sum(nums)
|
|
# min subarray (non-empty, non-whole? standard: min subarray can be whole -> circular empty; handle)
|
|
min_ending = min_so_far = nums[0]
|
|
for x in nums[1:]:
|
|
min_ending = min(x, min_ending + x)
|
|
min_so_far = min(min_so_far, min_ending)
|
|
# if all elements positive, min_subarray == total -> circular would be 0 (empty); use normal
|
|
circ = total - min_so_far
|
|
normal = kadane(nums)
|
|
if circ == 0:
|
|
return normal
|
|
return max(normal, circ)
|