Week 6: Binary Search, Peaks, Flags, Sieve
Week 6: Binary Search, Peaks, Flags, Sieve
Focus: harder Codility topics
Day 36: Binary Search
Learning Objectives
- Master binary search algorithm
- Apply binary search to optimization problems
- Recognize when binary search helps
Binary Search Basics
Binary Search: Find element in sorted array in O(log n) time.
How it works: Repeatedly divide search space in half.
function binarySearch(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
return mid; // Found!
}
if (arr[mid] < target) {
left = mid + 1; // Search right half
} else {
right = mid - 1; // Search left half
}
}
return -1; // Not found
}
// Test
const sorted = [1, 3, 5, 7, 9, 11, 13];
console.log(binarySearch(sorted, 7)); // 3
console.log(binarySearch(sorted, 10)); // -1
Time: O(log n), Space: O(1)
Binary Search Variants
Find First Occurrence:
function findFirst(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
let result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
result = mid;
right = mid - 1; // Continue searching left
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
Find Last Occurrence:
function findLast(arr: number[], target: number): number {
let left = 0;
let right = arr.length - 1;
let result = -1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (arr[mid] === target) {
result = mid;
left = mid + 1; // Continue searching right
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return result;
}
Binary Search on Answer
Pattern: When answer is in range [min, max], binary search on possible answers.
Example: Minimum Maximum Problem
// Can we divide array into K parts where max sum β€ mid?
function canDivide(arr: number[], K: number, maxSum: number): boolean {
let parts = 1;
let currentSum = 0;
for (const num of arr) {
if (num > maxSum) return false;
if (currentSum + num > maxSum) {
parts++;
currentSum = num;
if (parts > K) return false;
} else {
currentSum += num;
}
}
return true;
}
function minMaxSum(arr: number[], K: number): number {
let left = Math.max(...arr); // Minimum possible max
let right = arr.reduce((a, b) => a + b); // Maximum possible max
let result = right;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (canDivide(arr, K, mid)) {
result = mid;
right = mid - 1; // Try smaller max
} else {
left = mid + 1;
}
}
return result;
}
Codility: MinMaxDivision (Optional)
Problem: Divide array into K blocks minimizing largest sum.
Solution: Binary search on the answer!
function solution(K: number, M: number, A: number[]): number {
// Binary search on minimum possible largest sum
let left = Math.max(...A); // At least max element
let right = A.reduce((a, b) => a + b); // At most total sum
let result = right;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (canDivide(A, K, mid)) {
result = mid;
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
function canDivide(A: number[], K: number, maxSum: number): boolean {
let blocks = 1;
let currentSum = 0;
for (const num of A) {
if (currentSum + num > maxSum) {
blocks++;
currentSum = num;
if (blocks > K) return false;
} else {
currentSum += num;
}
}
return true;
}
Day 37: Sieve of Eratosthenes
Prime Numbers
Prime: Number > 1 divisible only by 1 and itself.
Examples: - Primes: 2, 3, 5, 7, 11, 13, 17, 19, 23... - Not primes: 4, 6, 8, 9, 10, 12, 14, 15, 16...
Naive Prime Check
function isPrime(n: number): boolean {
if (n < 2) return false;
if (n === 2) return true;
if (n % 2 === 0) return false;
// Check odd divisors up to βn
for (let i = 3; i * i <= n; i += 2) {
if (n % i === 0) return false;
}
return true;
}
// Time: O(βn)
Sieve of Eratosthenes
Find all primes up to N efficiently:
function sieve(n: number): boolean[] {
// isPrime[i] = true if i is prime
const isPrime = new Array(n + 1).fill(true);
isPrime[0] = isPrime[1] = false;
for (let i = 2; i * i <= n; i++) {
if (isPrime[i]) {
// Mark all multiples of i as not prime
for (let j = i * i; j <= n; j += i) {
isPrime[j] = false;
}
}
}
return isPrime;
}
// Get list of prime numbers
function getPrimes(n: number): number[] {
const isPrime = sieve(n);
const primes: number[] = [];
for (let i = 2; i <= n; i++) {
if (isPrime[i]) {
primes.push(i);
}
}
return primes;
}
console.log(getPrimes(30));
// [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
Time: O(n log log n), Space: O(n)
Codility Challenge: CountFactors
Problem: Count divisors of N.
Example:
N = 24
Divisors: 1, 2, 3, 4, 6, 8, 12, 24
Count: 8
Solution:
function solution(N: number): number {
let count = 0;
let i = 1;
// Check divisors up to βN
while (i * i < N) {
if (N % i === 0) {
count += 2; // Both i and N/i are divisors
}
i++;
}
// Check if N is perfect square
if (i * i === N) {
count++; // βN is a divisor
}
return count;
}
// Test cases
console.log(solution(24)); // 8
console.log(solution(1)); // 1
console.log(solution(16)); // 5 (1, 2, 4, 8, 16)
Time: O(βN), Space: O(1)
Codility Challenge: CountSemiprimes
Problem: Count semiprimes in ranges.
Semiprime: Product of exactly two primes (not necessarily distinct). - Examples: 4 (2Γ2), 6 (2Γ3), 9 (3Γ3), 10 (2Γ5), 15 (3Γ5)
Solution:
function solution(N: number, P: number[], Q: number[]): number[] {
// Find all primes up to N
const isPrime = sieve(N);
// Mark semiprimes
const isSemiprime = new Array(N + 1).fill(false);
for (let i = 2; i * i <= N; i++) {
if (isPrime[i]) {
for (let j = i; i * j <= N; j++) {
if (isPrime[j]) {
isSemiprime[i * j] = true;
}
}
}
}
// Build prefix sum for counting
const prefix = new Array(N + 1).fill(0);
for (let i = 1; i <= N; i++) {
prefix[i] = prefix[i - 1] + (isSemiprime[i] ? 1 : 0);
}
// Answer queries
const result: number[] = [];
for (let i = 0; i < P.length; i++) {
result.push(prefix[Q[i]] - prefix[P[i] - 1]);
}
return result;
}
function sieve(n: number): boolean[] {
const isPrime = new Array(n + 1).fill(true);
isPrime[0] = isPrime[1] = false;
for (let i = 2; i * i <= n; i++) {
if (isPrime[i]) {
for (let j = i * i; j <= n; j += i) {
isPrime[j] = false;
}
}
}
return isPrime;
}
Day 38: Peaks
Codility Challenge: Peaks
Problem: Divide array into equal blocks where each block contains a peak.
Peak: Element larger than neighbors
- A[i] is peak if A[i-1] < A[i] > A[i+1]
Example:
A = [1, 2, 3, 4, 3, 4, 1, 2, 3, 4, 6, 2]
Peaks at indices: 3, 5, 10
β β β
Can we divide into 3 blocks of size 4?
Block 1 [0..3]: has peak at 3 β
Block 2 [4..7]: has peak at 5 β
Block 3 [8..11]: has peak at 10 β
Answer: 3 (maximum blocks)
Solution:
function solution(A: number[]): number {
const n = A.length;
if (n < 3) return 0;
// Find all peaks
const peaks: number[] = [];
for (let i = 1; i < n - 1; i++) {
if (A[i] > A[i - 1] && A[i] > A[i + 1]) {
peaks.push(i);
}
}
if (peaks.length === 0) return 0;
// Try each possible number of blocks
// Number of blocks must divide n evenly
for (let numBlocks = peaks.length; numBlocks >= 1; numBlocks--) {
if (n % numBlocks !== 0) continue;
const blockSize = n / numBlocks;
let validBlocks = 0;
let nextPeak = 0;
// Check if each block has at least one peak
for (let block = 0; block < numBlocks; block++) {
const blockStart = block * blockSize;
const blockEnd = blockStart + blockSize;
// Skip peaks before this block
while (nextPeak < peaks.length && peaks[nextPeak] < blockStart) {
nextPeak++;
}
// Check if there's a peak in this block
if (nextPeak < peaks.length && peaks[nextPeak] < blockEnd) {
validBlocks++;
}
}
// If all blocks have peaks, we found answer
if (validBlocks === numBlocks) {
return numBlocks;
}
}
return 0;
}
// Test cases
console.log(solution([1, 2, 3, 4, 3, 4, 1, 2, 3, 4, 6, 2])); // 3
console.log(solution([1, 2, 1, 2, 1])); // 3
console.log(solution([1, 2, 3])); // 0
Time: O(n log n), Space: O(n)
Day 39: Flags β VERY COMMON!
Codility Challenge: Flags
Problem: Place maximum flags on peaks with distance β₯ K between flags, where K = number of flags.
Constraint: Distance between any two flags β₯ K (where K is the number of flags you're placing)
Example:
A = [1, 5, 3, 4, 3, 4, 1, 2, 3, 4, 6, 2]
Peaks at: 1, 3, 5, 10
Try K=3 flags:
- Place flag at peak 1
- Next flag must be β₯ 3 positions away β peak 5 (distance = 4) β
- Next flag must be β₯ 3 positions away β peak 10 (distance = 5) β
- Placed 3 flags! β
Try K=4 flags:
- Place flag at peak 1
- Next must be β₯ 4 away β peak 5 (distance = 4) β
- Next must be β₯ 4 away β peak 10 (distance = 5) β
- Only placed 3 flags, but need 4 β
Maximum: 3 flags
Solution:
function solution(A: number[]): number {
const n = A.length;
if (n < 3) return 0;
// Find all peaks
const peaks: number[] = [];
for (let i = 1; i < n - 1; i++) {
if (A[i] > A[i - 1] && A[i] > A[i + 1]) {
peaks.push(i);
}
}
if (peaks.length <= 1) return peaks.length;
// Maximum possible flags: βdistance between first and last peak
const maxFlags = Math.floor(Math.sqrt(peaks[peaks.length - 1] - peaks[0])) + 1;
// Try each possible number of flags (starting from max)
for (let K = Math.min(maxFlags, peaks.length); K >= 1; K--) {
let flagsPlaced = 0;
let lastFlag = -Infinity;
for (const peak of peaks) {
if (peak >= lastFlag + K) {
flagsPlaced++;
lastFlag = peak;
if (flagsPlaced === K) {
return K; // Found it!
}
}
}
}
return 0;
}
// Test cases
console.log(solution([1, 5, 3, 4, 3, 4, 1, 2, 3, 4, 6, 2])); // 3
console.log(solution([1, 2, 1, 2, 1, 2, 1])); // 3
console.log(solution([1, 2, 3, 4, 5])); // 0 (no peaks)
Time: O(n), Space: O(n)
Why βdistance: If we have flags at distance K apart, we can place ~distance/K flags. When distance/K = K, then K = βdistance.
Day 40: Rectangle / Geometry
Codility: MinPerimeterRectangle
Problem: Find minimal perimeter of rectangle with area N.
Example:
N = 30
Rectangles:
1 Γ 30: perimeter = 62
2 Γ 15: perimeter = 34
3 Γ 10: perimeter = 26
5 Γ 6: perimeter = 22 β minimum!
Solution:
function solution(N: number): number {
let minPerimeter = Infinity;
let i = 1;
while (i * i <= N) {
if (N % i === 0) {
const width = i;
const height = N / i;
const perimeter = 2 * (width + height);
minPerimeter = Math.min(minPerimeter, perimeter);
}
i++;
}
return minPerimeter;
}
// Test cases
console.log(solution(30)); // 22
console.log(solution(36)); // 24 (6Γ6)
console.log(solution(1)); // 4
Time: O(βN), Space: O(1)
Codility: ChocolatesByNumbers
Problem: X chocolates in circle. Eat every Mth. How many until you eat one you've eaten?
Example:
N = 10, M = 4
Chocolates: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Eat: 0 β 4 β 8 β 2 β 6 β 0 (stop!)
Count: 5
Key Insight: Answer = N / GCD(N, M)
Solution:
function solution(N: number, M: number): number {
return N / gcd(N, M);
}
function gcd(a: number, b: number): number {
if (b === 0) return a;
return gcd(b, a % b);
}
// Test cases
console.log(solution(10, 4)); // 5
console.log(solution(10, 3)); // 10
console.log(solution(947, 4228)); // 947
Time: O(log(min(N, M))), Space: O(1)
Day 41-42: Review and Mock Test
Key Patterns Review
Binary Search: - Sorted array: O(log n) search - Binary search on answer: try values in range [min, max]
Sieve of Eratosthenes: - Find all primes up to N - O(n log log n) time
Peaks and Flags: - Find peaks first - Try different block sizes/flag counts - Greedy placement
Mock Test (75-90 min)
Problem 1: Flags (30 min) Problem 2: CountFactors (20 min) Problem 3: MaxSliceSum (25 min - review from Week 5)
Week 6 Summary
What You Learned
β Binary search algorithm and variants β Binary search on answer space β Sieve of Eratosthenes for primes β Peak finding and block division β Flags problem (greedy + optimization) β GCD and number theory basics
Critical Patterns
1. Binary Search - On sorted data: O(log n) - On answer space: try values efficiently
2. Sieve for Primes - Preprocess once, answer many queries - Combine with prefix sums
3. Greedy with Constraints - Flags: place greedily with distance check - Peaks: check each possible division
Why This Week Was Important
These topics appear in: - 25%+ of medium/hard Codility tests - Optimization problems - Number theory questions
Must master: - Flags (very common!) - Binary search template - Sieve of Eratosthenes
Prepare for Week 7
Next week: Review and reinforcement - Revisit difficult problems - Increase speed - Fill knowledge gaps
Almost there! Week 6 complete! π―