Apache Spark's Python API uses lazy iteration and generator expressions to process datasets that are larger than available RAM, letting data scientists at Netflix run machine learning jobs on petabytes of viewing history on standard hardware without loading the entire dataset at once. Generators produce one value at a time on demand, so a pipeline processing a billion rows uses only as much memory as a single row. The advanced iteration patterns in this lesson, including itertools and generator expressions, are the foundation of that memory-efficient approach.
Multiple Pointers
Daily Life
Interviews
Solve pair problems in a single pass
The two-pointer technique uses two index variables to traverse a sequence simultaneously. Rather than nested loops that check every pair (O(n²) complexity), two pointers can solve many problems in a single pass (O(n) complexity). This technique is especially powerful with sorted sequences.
Pointers from Both Ends
The most common pattern uses one pointer at the start and one at the end, moving them toward each other. This efficiently solves problems like finding pairs, checking palindromes, and partitioning arrays:
1
# Find if any two numbers sum to target
2
defhas_pair_with_sum(sorted_nums,target):
3
left=0
4
right=len(sorted_nums)-1
5
6
whileleft<right:
7
current_sum=sorted_nums[left]+sorted_nums[right]
8
ifcurrent_sum==target:
9
return(sorted_nums[left],sorted_nums[right])
10
elifcurrent_sum<target:
11
# Need larger sum
12
left+=1
13
else:
14
# Need smaller sum
15
right-=1
16
17
returnNone
18
19
# Test with sorted list
20
numbers=[1,3,5,7,9,11,15]
21
print(has_pair_with_sum(numbers,12))
22
print(has_pair_with_sum(numbers,20))
23
print(has_pair_with_sum(numbers,50))
>>>Output
(1, 11)
(5, 15)
None
Because the list is sorted, we know that if the sum is too small, we need a larger left value. If too large, we need a smaller right value. This lets us eliminate many possibilities with each comparison.
Palindrome Check
Checking if a string is a palindrome is a classic two-pointer problem. Compare characters from both ends, moving inward:
1
defis_palindrome(text):
2
# Clean: lowercase, letters only
3
cleaned="".join(c.lower()forcintextifc.isalnum())
4
5
left=0
6
right=len(cleaned)-1
7
8
whileleft<right:
9
ifcleaned[left]!=cleaned[right]:
10
returnFalse
11
left+=1
12
right-=1
13
14
returnTrue
15
16
# Test cases
17
print(is_palindrome("racecar"))
18
print(is_palindrome("A man, a plan, a canal: Panama"))
19
print(is_palindrome("hello"))
20
print(is_palindrome("Was it a car or a cat I saw"))
>>>Output
True
True
False
True
Fast and Slow Pointers
Another pattern uses two pointers moving at different speeds. The fast pointer explores ahead while the slow pointer tracks a different position. This solves problems like removing duplicates in-place:
1
defremove_duplicates(sorted_list):
2
"""Remove duplicates in-place."""
3
ifnotsorted_list:
4
return0
5
6
slow=0
7
8
forfastinrange(1,len(sorted_list)):
9
ifsorted_list[fast]!=sorted_list[slow]:
10
slow+=1
11
sorted_list[slow]=sorted_list[fast]
12
13
# Elements 0 through slow are unique
14
returnslow+1
15
16
# Test
17
nums=[1,1,2,2,2,3,4,4,5]
18
new_length=remove_duplicates(nums)
19
print("Unique count:",new_length)
20
print("Unique values:",nums[:new_length])
>>>Output
Unique count: 5
Unique values: [1, 2, 3, 4, 5]
TIP
The fast-slow pointer pattern is also called the "runner" technique. The fast pointer scans ahead while the slow pointer marks where to place the next valid element. This enables in-place modifications without extra memory.
Three Pointers
Some problems require three pointers. The classic example is partitioning an array into three sections, like the Dutch National Flag problem for sorting colors:
1
defdutch_flag_sort(arr):
2
"""Sort array of 0s, 1s, 2s in single pass"""
3
low=0
4
mid=0
5
high=len(arr)-1
6
7
whilemid<=high:
8
ifarr[mid]==0:
9
arr[low],arr[mid]=arr[mid],arr[low]
10
low+=1
11
mid+=1
12
elifarr[mid]==1:
13
mid+=1
14
else:
15
arr[mid],arr[high]=arr[high],arr[mid]
16
high-=1
17
18
returnarr
19
20
# Test
21
colors=[2,0,1,2,1,0,0,2,1]
22
print("Before:",colors.copy())
23
print("After:",dutch_flag_sort(colors))
>>>Output
Before: [2, 0, 1, 2, 1, 0, 0, 2, 1]
After: [0, 0, 0, 1, 1, 1, 2, 2, 2]
The two-pointer technique applies to many classic problems. Each variant uses a different pointer movement strategy:
Pair sum in sorted arrays
Left and right pointers converge toward each other to find target sums
Merge two sorted arrays
One pointer per array, advancing whichever has the smaller current value
Remove duplicates in place
Fast pointer scans ahead while slow pointer marks the write position
Partition arrays
Three-way partitioning sorts elements into regions in a single pass
Sliding Window Pattern
Daily Life
Interviews
Analyze contiguous subsequences in O(n)
The sliding window pattern maintains a "window" over a contiguous portion of a sequence. The window slides through the data, adding elements at one end and removing them from the other. This efficiently solves problems involving contiguous subarrays or substrings.
Fixed-Size Window
The simplest form uses a fixed window size. Rather than recalculating from scratch for each position, we update the window incrementally:
1
defmax_sum_subarray(nums,k):
2
"""Max sum of k consecutive."""
3
iflen(nums)<k:
4
returnNone
5
6
# Calculate sum of first window
7
window_sum=sum(nums[:k])
8
max_sum=window_sum
9
10
# Slide the window
11
foriinrange(k,len(nums)):
12
# Add new element, remove old element
13
window_sum+=nums[i]-nums[i-k]
14
max_sum=max(max_sum,window_sum)
15
16
returnmax_sum
17
18
# Test
19
values=[1,4,2,10,2,3,1,0,20]
20
print("Max sum of 3:",max_sum_subarray(values,3))
21
print("Max sum of 4:",max_sum_subarray(values,4))
>>>Output
Max sum of 3: 23
Max sum of 4: 24
Instead of recalculating sum() for each window position (O(n×k) time), we update incrementally by adding the new element and subtracting the element that left the window (O(n) time). For large k, this is dramatically faster.
The fixed-size sliding window follows a simple four-step recipe on every iteration:
01
Initialize window
Compute the aggregate for the first k elements as your starting state
02
Add new element
Include the element entering the window at the right edge
03
Remove old element
Exclude the element leaving the window at the left edge
04
Update best result
Compare the current window value against the running optimum
Moving Average
A practical application is calculating moving averages, common in time series analysis and financial data:
1
defmoving_average(data,window_size):
2
"""Calculate moving average for each position"""
3
iflen(data)<window_size:
4
return[]
5
6
result=[]
7
window_sum=sum(data[:window_size])
8
result.append(window_sum/window_size)
9
10
foriinrange(window_size,len(data)):
11
window_sum+=data[i]-data[i-window_size]
12
result.append(window_sum/window_size)
13
14
returnresult
15
16
# Stock prices over days
17
prices=[100,102,104,103,105,107,106,108,110]
18
ma3=moving_average(prices,3)
19
print("3-day moving averages:")
20
fori,avginenumerate(ma3):
21
print(" Day",i+3,":",round(avg,2))
>>>Output
3-day moving averages:
Day 3 : 102.0
Day 4 : 103.0
Day 5 : 104.0
Day 6 : 105.0
Day 7 : 106.0
Day 8 : 107.0
Day 9 : 108.0
Variable-Size Window
More complex problems use a variable-size window that expands and contracts based on conditions. This is powerful for finding optimal subarrays:
1
deflongest_subarray_sum(nums,max_sum):
2
"""Find length of longest subarray with sum <= max_sum"""
3
left=0
4
current_sum=0
5
max_length=0
6
7
forrightinrange(len(nums)):
8
current_sum+=nums[right]
9
10
# Shrink window while sum exceeds limit
11
whilecurrent_sum>max_sumandleft<=right:
12
current_sum-=nums[left]
13
left+=1
14
15
max_length=max(max_length,right-left+1)
16
17
returnmax_length
18
19
values=[1,2,3,4,5]
20
print("Max length with sum <= 6:",longest_subarray_sum(values,6))
21
print("Max length with sum <= 9:",longest_subarray_sum(values,9))
22
print("Max length with sum <= 15:",longest_subarray_sum(values,15))
>>>Output
Max length with sum <= 6: 3
Max length with sum <= 9: 4
Max length with sum <= 15: 5
The right pointer expands the window, and the left pointer contracts it when the constraint is violated. This finds the optimal window in O(n) time.
Unique Character Substrings
A classic interview problem is finding the longest substring without repeating characters. The variable window tracks character counts:
1
deflongest_unique_substring(s):
2
"""Find longest substring with all unique characters"""
3
char_index={}
4
left=0
5
max_length=0
6
max_start=0
7
8
forright,charinenumerate(s):
9
# If char was seen and is in current window
10
ifcharinchar_indexandchar_index[char]>=left:
11
left=char_index[char]+1
12
13
char_index[char]=right
14
15
ifright-left+1>max_length:
16
max_length=right-left+1
17
max_start=left
18
19
returns[max_start:max_start+max_length]
20
21
print(longest_unique_substring("abcabcbb"))
22
print(longest_unique_substring("bbbbb"))
23
print(longest_unique_substring("pwwkew"))
>>>Output
abc
b
wke
Python Quiz
> Compute the sum of the first three elements as a sliding window start, then find the overall maximum. Pick the aggregation for the window, and the one that scans the entire list.
The sliding window pattern shines when the data stream is continuous and you cannot afford to reprocess earlier elements. Initializing the first window correctly is the foundation, and then each step only updates what changed.
Combining sum() for windows and max() for full scans are common patterns in data analysis. They map cleanly onto SQL aggregations like SUM() OVER and MAX(), making this mental model useful across tools.
Fixed-size windows suit problems with a known frame size, like a 7-day rolling average. Variable-size windows suit problems with a constraint, like "all elements under a budget", and are solved with the expand-shrink loop pattern.
Reverse Iteration
Daily Life
Interviews
Traverse sequences from end to start
Iterating backwards through sequences is often necessary for algorithms that build results from end to beginning, or when modifications affect indices of later elements. Python provides several ways to iterate in reverse.
The reversed() Function
The reversed() function returns an iterator that yields elements in reverse order. It works on any sequence without creating a copy:
1
# Reverse iteration with reversed()
2
items=["first","second","third","fourth"]
3
4
foriteminreversed(items):
5
print(item)
6
7
print("---")
8
9
# Works with strings
10
forcharinreversed("Python"):
11
print(char,end=" ")
12
print()
13
14
# Original is unchanged
15
print("Original:",items)
>>>Output
fourth
third
second
first
---
n o h t y P
Original: ['first', 'second', 'third', 'fourth']
range() with Negative Step
For index-based reverse iteration, use range() with a negative step:
1
# Count down from 5 to 1
2
foriinrange(5,0,-1):
3
print(i)
4
5
print("---")
6
7
# Access list indices in reverse
8
nums=[10,20,30,40,50]
9
foriinrange(len(nums)-1,-1,-1):
10
print("Index",i,"=",nums[i])
>>>Output
5
4
3
2
1
---
Index 4 = 50
Index 3 = 40
Index 2 = 30
Index 1 = 20
Index 0 = 10
Note: range(len(nums) - 1, -1, -1) goes from the last index down to 0. The stop value is -1 (not included) because we want to include index 0.
Why Reverse Iteration
Reverse iteration is essential when modifying a list based on indices. Deleting from the beginning shifts all later indices, but deleting from the end keeps earlier indices valid:
1
# Remove all even numbers by iterating backwards
2
numbers=[1,2,3,4,5,6,7,8,9,10]
3
4
# Iterate backwards so deletions don't affect remaining indices
5
foriinrange(len(numbers)-1,-1,-1):
6
ifnumbers[i]%2==0:
7
delnumbers[i]
8
9
print("After removing evens:",numbers)
10
11
# Another example: remove elements that match previous
12
data=["a","a","b","b","b","c","c"]
13
foriinrange(len(data)-1,0,-1):
14
ifdata[i]==data[i-1]:
15
deldata[i]
16
17
print("After removing consecutive dupes:",data)
>>>Output
After removing evens: [1, 3, 5, 7, 9]
After removing consecutive dupes: ['a', 'b', 'c']
This code tries to remove even numbers while iterating forward, but a stale index causes it to skip elements. Fix the loop direction so that every even number is removed safely.
Debug Challenge
> This code tries to remove even numbers while iterating forward, but skips elements due to shifting indices. Fix the loop direction.
IndexError: list index out of range because forward deletion shifts indices
Some algorithms naturally build results from end to beginning. Reverse iteration aligns the logic with the output order:
1
defreverse_words(sentence):
2
"""Reverse words in a sentence"""
3
words=sentence.split()
4
result=[]
5
6
foriinrange(len(words)-1,-1,-1):
7
result.append(words[i])
8
9
return" ".join(result)
10
11
print(reverse_words("Hello World"))
12
print(reverse_words("The quick brown fox"))
13
14
# Calculate running sum from end
15
values=[1,2,3,4,5]
16
suffix_sums=[0]*len(values)
17
18
running=0
19
foriinrange(len(values)-1,-1,-1):
20
running+=values[i]
21
suffix_sums[i]=running
22
23
print("Values:",values)
24
print("Suffix sums:",suffix_sums)
>>>Output
World Hello
fox brown quick The
Values: [1, 2, 3, 4, 5]
Suffix sums: [15, 14, 12, 9, 5]
•Use reversed()
Simple reverse iteration
No index needed
Read-only access
Cleaner syntax
•Use range() Negative
Need the index value
Modifying by index
Deleting elements
Complex index math
Modifying While Looping
Daily Life
Interviews
Safely change collections mid-loop
Modifying a collection while iterating over it is dangerous and often causes bugs. Elements get skipped or the iterator becomes invalid. However, with the right techniques, you can safely modify collections during iteration.
Before looking at specific techniques, here are the three safe strategies you can rely on whenever you need to change a collection mid-loop.
Three Safe Modification Strategies
Iterate over a copy (list[:] or list(dict.keys())) while modifying the original.
Loop backwards with range(len-1, -1, -1) so deletions do not shift unvisited indices.
Build a brand-new collection with a list comprehension and replace the old one.
The Problem
Removing elements while iterating forward causes items to be skipped because indices shift:
1
# Problem: removing while iterating
2
# This shows what goes wrong:
3
4
nums=[2,4,6,8]
5
result=[]
6
fornuminnums:
7
ifnum%2==0:
8
result.append(num)
9
10
# What happens if we tried removing:
11
test=[2,4,6,8]
12
# Safe: iterate over a copy
13
fornuminlist(test):
14
ifnum%2==0:
15
test.remove(num)
16
print("After unsafe pattern would give: [4, 8]")
17
print("After safe pattern (copy):",test)
>>>Output
After unsafe pattern would give: [4, 8]
After safe pattern (copy): []
When you remove index 0, element at index 1 moves to index 0. But the iterator advances to index 1, skipping what is now at index 0.
Solution 1: Copy, Then Iter
Create a copy of the list and iterate over it while modifying the original:
1
# Iterate over copy, modify original
2
nums=[2,4,6,8,10]
3
4
fornuminnums[:]:
5
ifnum%4==0:
6
nums.remove(num)
7
8
print("After removing multiples of 4:",nums)
9
10
# Alternative: explicit copy
11
items=["keep","remove","keep","remove"]
12
foriteminlist(items):
13
ifitem=="remove":
14
items.remove(item)
15
16
print("After filtering:",items)
>>>Output
After removing multiples of 4: [2, 6, 10]
After filtering: ['keep', 'keep']
Solution 2: Reverse Iter
As shown earlier, iterating backwards keeps all earlier indices valid after deletion:
1
# Reverse iteration for safe deletion
2
nums=[1,2,3,4,5,6,7,8]
3
4
foriinrange(len(nums)-1,-1,-1):
5
ifnums[i]%2==0:
6
delnums[i]
7
8
print("After removing evens:",nums)
>>>Output
After removing evens: [1, 3, 5, 7]
Solution 3: New Collection
The most Pythonic approach is often to build a new collection rather than modify the existing one:
1
# Build new list (list comprehension)
2
nums=[1,2,3,4,5,6,7,8]
3
odds=[nforninnumsifn%2!=0]
4
print("Odds:",odds)
5
6
# Or with filter
7
nums=[1,2,3,4,5,6,7,8]
8
odds=list(filter(lambdan:n%2!=0,nums))
9
print("Odds (filter):",odds)
10
11
# Transform and filter together
12
data=[" hello ",""," world "," ","python"]
13
cleaned=[s.strip()forsindataifs.strip()]
14
print("Cleaned:",cleaned)
>>>Output
Odds: [1, 3, 5, 7]
Odds (filter): [1, 3, 5, 7]
Cleaned: ['hello', 'world', 'python']
TIP
List comprehensions are generally preferred over in-place modifications. They are clearer, create fewer bugs, and often perform better because Python optimizes list comprehension creation.
Modifying Dictionaries
Dictionary modification during iteration has the same issues. Use dict.copy() or build a new dict:
Choosing the right modification strategy prevents silent data corruption. Follow these guidelines to keep your iterations safe:
✓Do
Iterate over list[:] copy to modify original
Use reverse iteration for index-based deletes
Build new collections with comprehensions
Collect keys first, then modify the dict
✗Don't
Remove items while iterating forward
Add keys to a dict during iteration
Delete by index while looping forward
Assume iterator stays valid after changes
Python Quiz
> Safely remove negative numbers from a list by iterating over a copy. Pick the function that creates a snapshot of the list, and the method that deletes a specific value.
Iterating over list(items) is a clear, readable pattern that signals to other developers that you intend to modify the original collection during the loop.
For bulk filtering, a list comprehension is often cleaner than the copy-and-remove pattern. Write [x for x in items if condition] instead of modifying items in a loop when readability matters most.
Dictionary modification during iteration raises RuntimeError in Python 3. Always iterate over list(d.keys()) or use a dictionary comprehension to build a new dict with the desired entries.
Using any() and all()
Daily Life
Interviews
Test conditions across entire sequences
The built-in functions any() and all() test conditions across entire sequences. They replace common loop patterns with concise, readable, and efficient expressions. Both short-circuit, meaning they stop as soon as the result is determined.
These two functions cover the vast majority of sequence-wide condition checks you will ever need:
any()all()not any()not all()
any()
At least one
True if one item is truthy
all()
Every item
True if all items are truthy
not any()
None at all
True if nothing is truthy
not all()
Some falsy
True if any item is falsy
The any() Function
any(iterable) returns True if at least one element is truthy. It stops at the first True value:
1
# Basic any() usage
2
print(any([False,False,True,False]))
3
print(any([False,False,False]))
4
print(any([]))
5
6
# With conditions using generator expression
7
nums=[2,4,6,8,10]
8
has_odd=any(n%2!=0forninnums)
9
print("Has odd number:",has_odd)
10
11
nums=[2,4,5,8,10]
12
has_odd=any(n%2!=0forninnums)
13
print("Has odd number:",has_odd)
14
15
# Check if any string is empty
16
strings=["hello","world","","python"]
17
has_empty=any(s==""forsinstrings)
18
print("Has empty string:",has_empty)
>>>Output
True
False
False
Has odd number: False
Has odd number: True
Has empty string: True
The all() Function
all(iterable) returns True only if all elements are truthy. It stops at the first False value:
1
# Basic all() usage
2
print(all([True,True,True]))
3
print(all([True,False,True]))
4
print(all([]))
5
6
# All positive numbers?
7
nums=[1,2,3,4,5]
8
all_positive=all(n>0forninnums)
9
print("All positive:",all_positive)
10
11
nums=[1,2,-3,4,5]
12
all_positive=all(n>0forninnums)
13
print("All positive:",all_positive)
14
15
# Validate data
16
users=[
17
{"name":"Alice","age":30},
18
{"name":"Bob","age":25},
19
{"name":"Charlie","age":35},
20
]
21
all_adults=all(u["age"]>=18foruinusers)
22
all_have_names=all(u.get("name")foruinusers)
23
print("All adults:",all_adults)
24
print("All have names:",all_have_names)
>>>Output
True
False
True
All positive: True
All positive: False
All adults: True
All have names: True
Replacing Loop Patterns
Many common loop patterns can be replaced with any() or all():
1
nums=[10,20,35,40,50]
2
3
# Instead of loop with break
4
# found = False
5
# for n in nums:
6
# if n > 30:
7
# found = True
8
# break
9
10
# Use any()
11
found=any(n>30forninnums)
12
print("Found > 30:",found)
13
14
# Instead of loop checking all
15
# valid = True
16
# for n in nums:
17
# if n < 0:
18
# valid = False
19
# break
20
21
# Use all()
22
valid=all(n>=0forninnums)
23
print("All non-negative:",valid)
>>>Output
Found > 30: True
All non-negative: True
Try switching between any() and all() to see how each evaluates the same list of values differently:
Fill in the Blank
> The functions any() and all() check conditions across a list. Pick one to see how each evaluates the same data differently.
nums = [2, 4, 7, 8, 10]
result = (n % 2 == 0 for n in nums)
print(result)
Validation Examples
These functions shine in data validation scenarios:
Here is a side-by-side comparison of how any() and all() evaluate elements.
•any()
True if ANY element is truthy
Stops at first True (short-circuit)
Empty sequence = False
Like OR across all elements
•all()
True if ALL elements are truthy
Stops at first False (short-circuit)
Empty sequence = True
Like AND across all elements
Advanced iteration techniques let you handle complex data processing with elegance and efficiency. Put your skills to the test with hands-on challenges in the Python Builder.
The Real-Time Log AnalyzerStep 1
>
You are the data engineer at a cloud hosting company that monitors 500 servers. Each server emits log events every second: timestamps, CPU usage, memory usage, and error codes. The operations team needs real-time alerting when a server shows sustained high CPU (above 90% for 30 consecutive seconds), memory leaks (steadily increasing memory over 5-minute windows), or error bursts (more than 10 errors in any 60-second window). The current monitoring script processes logs one at a time and is falling behind during peak hours.
server_logs
timestamp
server_id
cpu_pct
mem_mb
error_code
14:00:01
srv-042
92
2048
null
14:00:02
srv-042
95
2052
E-504
14:00:03
srv-042
91
2058
null
May
2026
CPU Alert Logic
The first requirement is detecting when a server stays above 90% CPU for 30 consecutive seconds. With 500 servers emitting one log per second, that is 500 events per second. How should you track the consecutive high-CPU count per server?
any() and all() are the clearest way to express intent when checking conditions across a collection. They read like English and short-circuit early, making them both expressive and efficient.
Nesting any() inside all() or vice versa lets you express matrix-level conditions concisely: "every row has at least one even number" becomes all(any(n % 2 == 0 for n in row) for row in matrix).
In production monitoring systems, combining sliding windows with any() and all() checks gives you real-time alerting that scales to thousands of events per second without complex threading or external tools.
❯❯❯PUTTING IT ALL TOGETHER
> You are a senior data engineer at Snowflake processing millions of raw query event records to detect anomalous usage patterns, where loading everything into memory at once is not an option.
Multiple pointers scan the sorted event log from both ends simultaneously to find mismatched start and end timestamps without nested passes.
Sliding window maintains a fixed-size buffer of consecutive events to detect rate spikes across any contiguous time interval.
Reverse iteration with negative range() steps processes correction records in reverse chronological order, overwriting earlier entries without a list reversal copy.
any() and all() validate quality conditions across entire record batches in a single readable expression rather than explicit loops.
KEY TAKEAWAYS
Two-pointer technique uses left/right indices to traverse sorted sequences in O(n) time instead of O(n squared)
Sliding window pattern efficiently analyzes contiguous subsequences by updating incrementally
reversed() or range(len-1, -1, -1) for backward iteration
Iterate backwards when deleting elements to keep earlier indices valid
Never modify a collection while iterating forward; use a copy, reverse iteration, or build a new collection
any() returns True if at least one element is truthy; stops at first True
all() returns True only if all elements are truthy; stops at first False
Prefer list comprehensions over in-place modifications for cleaner, safer code
Algorithmic iteration techniques
Category
Python
Difficulty
advanced
Duration
34 minutes
Challenges
0 hands-on challenges
Topics covered: Multiple Pointers, Sliding Window Pattern, Reverse Iteration, Modifying While Looping, Using any() and all()
Pointers from Both Ends The most common pattern uses one pointer at the start and one at the end, moving them toward each other. This efficiently solves problems like finding pairs, checking palindromes, and partitioning arrays: Because the list is sorted, we know that if the sum is too small, we need a larger left value. If too large, we need a smaller right value. This lets us eliminate many possibilities with each comparison. Palindrome Check Checking if a string is a palindrome is a classic
The sliding window pattern maintains a "window" over a contiguous portion of a sequence. The window slides through the data, adding elements at one end and removing them from the other. This efficiently solves problems involving contiguous subarrays or substrings. Fixed-Size Window The simplest form uses a fixed window size. Rather than recalculating from scratch for each position, we update the window incrementally: The fixed-size sliding window follows a simple four-step recipe on every iterat
Iterating backwards through sequences is often necessary for algorithms that build results from end to beginning, or when modifications affect indices of later elements. Python provides several ways to iterate in reverse. The reversed() Function range() with Negative Step Why Reverse Iteration Reverse iteration is essential when modifying a list based on indices. Deleting from the beginning shifts all later indices, but deleting from the end keeps earlier indices valid: This code tries to remove
Modifying a collection while iterating over it is dangerous and often causes bugs. Elements get skipped or the iterator becomes invalid. However, with the right techniques, you can safely modify collections during iteration. Before looking at specific techniques, here are the three safe strategies you can rely on whenever you need to change a collection mid-loop. The Problem Removing elements while iterating forward causes items to be skipped because indices shift: When you remove index 0, eleme
These two functions cover the vast majority of sequence-wide condition checks you will ever need: The any() Function The all() Function Replacing Loop Patterns Try switching between any() and all() to see how each evaluates the same list of values differently: Validation Examples These functions shine in data validation scenarios: Combining any() and all() Complex conditions can combine both functions: Here is a side-by-side comparison of how any() and all() evaluate elements. Advanced iteration