Files

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)