Hello there! Welcome to this exciting TypeScript coding lesson, where we'll explore the performance efficiencies gained by utilizing Map
types. Our focus will be on solving a problem involving arrays, where we aim to make an optimal choice to minimize the size of segments. Ready to dive in? Let's get started!
In this unit's task, we're tasked with creating a TypeScript function named minimalMaxBlock()
. This function should accept an array of integers and compute an interesting property related to contiguous segments within the array.
Specifically, you'll need to select a particular integer, k
, from the array. By choosing k
, the function will remove all occurrences of k
from the array, splitting it into several contiguous segments. The unique aspect of k
is that it should be chosen such that the maximum length among these segments is minimized.
For example, with the list [1, 2, 2, 3, 1, 4, 4, 4, 1, 2, 5]
, removing all instances of 2 results in blocks [1]
, [3, 1, 4, 4, 4, 1]
, [5]
, with the longest containing six elements. However, if we remove all instances of 1, the remaining blocks are [2, 2, 3]
, [4, 4, 4]
, [2, 5]
, with the longest containing three elements. Therefore, the function should return 1, minimizing the maximal block length.
An initial approach to this problem is a brute force method. We can test each possible value in the array by removing it and checking the resulting sub-array sizes. This method involves iterating through the array for each possible k
.
TypeScript1function minimalMaxBlockBruteforce(array: number[]): number { 2 let minMaxBlockSize: number = Number.MAX_VALUE; 3 let minNum: number = -1; 4 5 const uniqueElements: Set<number> = new Set(array); 6 7 uniqueElements.forEach((num: number) => { 8 const indices: number[] = []; 9 for (let i: number = 0; i < array.length; ++i) { 10 if (array[i] === num) { 11 indices.push(i); 12 } 13 } 14 indices.unshift(-1); 15 indices.push(array.length); 16 17 let maxBlockSize: number = 0; 18 for (let i: number = 1; i < indices.length; ++i) { 19 maxBlockSize = Math.max(maxBlockSize, indices[i] - indices[i - 1] - 1); 20 } 21 22 if (maxBlockSize < minMaxBlockSize) { 23 minMaxBlockSize = maxBlockSize; 24 minNum = num; 25 } 26 }); 27 28 return minNum; 29}
This method has a time complexity of O(n^2)
due to two nested loops: an outer loop iterating through each potential k
value, and an inner one through the array for each k
.
To solve the problem efficiently, we need to track each number's positions, the block size when removing each element, and store the maximum of these blocks.
Here, we won't need all positions, just the last occurrence, since we're interested in blocks between two adjacent elements, between the start of the array and the first occurrence, and between the last occurrence and the array's end. We'll keep track of the maximum block size for each element, updating as necessary.
-
Initialize two
Map
s:lastOccurrence
to store the last positions of each number, andmaxBlockSizes
to map each number to its block's maximum size. -
Traverse the array:
- If a number is encountered for the first time, consider the block from the start to its position, storing the size in
maxBlockSizes
. - If the number has occurred before, calculate the block size it forms, updating
maxBlockSizes
if necessary. - Store the current index as the last occurrence of the number.
- If a number is encountered for the first time, consider the block from the start to its position, storing the size in
-
Calculate the "tail block" size (the block from the last occurrence of a number to the array's end) for each number and update
maxBlockSizes
accordingly. -
Return the number with the minimal maximum block size.
We'll initialize the Map
structures with proper key and value types specified.
TypeScript1function minimalMaxBlock(array: number[]): number { 2 const lastOccurrence: Map<number, number> = new Map(); 3 const maxBlockSizes: Map<number, number> = new Map();
We'll iterate over the array and apply logic with TypeScript types incorporated.
TypeScript1 for (let i: number = 0; i < array.length; ++i) { 2 const num: number = array[i]; 3 if (!lastOccurrence.has(num)) { 4 maxBlockSizes.set(num, i); 5 } else { 6 const blockSize: number = i - lastOccurrence.get(num)! - 1; 7 maxBlockSizes.set(num, Math.max(maxBlockSizes.get(num)!, blockSize)); 8 } 9 lastOccurrence.set(num, i); 10 }
Next, calculate and update tail block sizes with TypeScript annotations.
TypeScript1 lastOccurrence.forEach((pos: number, num: number) => { 2 const blockSize: number = array.length - pos - 1; 3 maxBlockSizes.set(num, Math.max(maxBlockSizes.get(num)!, blockSize)); 4 });
Finally, determine the number with the smallest block size and return it, fully typed.
TypeScript1 let minNum: number = -1; 2 let minBlockSize: number = Number.MAX_VALUE; 3 maxBlockSizes.forEach((blockSize: number, num: number) => { 4 if (blockSize < minBlockSize) { 5 minBlockSize = blockSize; 6 minNum = num; 7 } 8 }); 9 10 return minNum; 11}
Here's the complete TypeScript code for our optimized minimalMaxBlock
function, leveraging Map
for efficient traversal.
TypeScript1function minimalMaxBlock(array: number[]): number { 2 const lastOccurrence: Map<number, number> = new Map(); 3 const maxBlockSizes: Map<number, number> = new Map(); 4 5 for (let i: number = 0; i < array.length; ++i) { 6 const num: number = array[i]; 7 if (!lastOccurrence.has(num)) { 8 maxBlockSizes.set(num, i); 9 } else { 10 const blockSize: number = i - lastOccurrence.get(num)! - 1; 11 maxBlockSizes.set(num, Math.max(maxBlockSizes.get(num)!, blockSize)); 12 } 13 lastOccurrence.set(num, i); 14 } 15 16 lastOccurrence.forEach((pos: number, num: number) => { 17 const blockSize: number = array.length - pos - 1; 18 maxBlockSizes.set(num, Math.max(maxBlockSizes.get(num)!, blockSize)); 19 }); 20 21 let minNum: number = -1; 22 let minBlockSize: number = Number.MAX_VALUE; 23 maxBlockSizes.forEach((blockSize: number, num: number) => { 24 if (blockSize < minBlockSize) { 25 minBlockSize = blockSize; 26 minNum = num; 27 } 28 }); 29 30 return minNum; 31} 32 33// Example usage 34const exampleArray: number[] = [1, 2, 2, 3, 1, 4, 4, 4, 1, 2, 5]; 35const result: number = minimalMaxBlock(exampleArray); 36console.log(`The integer to remove for optimal segments is: ${result}`); 37// Expected output: The integer to remove for optimal segments is: 1
Well done! This unit tackled an interesting problem while exploring TypeScript's benefits — particularly its type system. By leveraging Map
and array manipulation, we've enhanced performance and accuracy. Your task is to apply this knowledge to solve ever more complex challenges. Let's continue this journey and master the art of TypeScript programming!