Introduction

Hello there! In this unit, we're offering an engaging coding lesson that highlights the performance efficiencies offered by utilizing efficient data structures in Go. We'll address a slice-based problem that requires us to make an optimal choice to minimize the size of our slice. Excited? So am I! Let's get started.

Task Statement

In this unit's task, we'll manipulate a slice of integers. You are required to construct a Go function titled MinimalMaxBlock(). This function should accept a slice as an input and compute an intriguing property related to contiguous blocks within the slice.

More specifically, you must select a particular integer, k, from the slice. Once you've selected k, the function should remove all occurrences of k from the slice, thereby splitting it into several contiguous blocks or remaining sub-slices. A unique feature of k is that it is chosen such that the maximum length among these blocks is minimized.

For instance, consider the slice [1, 2, 2, 3, 1, 4, 4, 4, 1, 2, 5]. If we eliminate all instances of 2 (our k), the remaining blocks would be [1], [3, 1, 4, 4, 4, 1], and [5], with the longest containing 6 elements. Now, if we instead remove all instances of 1, the new remaining blocks would be [2, 2, 3], [4, 4, 4], and [2, 5], the longest of which contains 3 elements. As such, the function should return 1 in this case, as it leads to a minimally maximal block length.

Brute Force Approach

An initial way to approach this problem can be through a brute force method. Each possible value in the slice could be tested in turn by removing it from the slice and then checking the resulting sub-slice sizes. This approach entails iteratively stepping through the slice for each possible value.

package main

import (
    "fmt"
    "math"
)

func MinimalMaxBlockBruteforce(slice []int) int {
    minMaxBlockSize := math.MaxInt32
    minNum := -1

    uniqueElements := make(map[int]bool)
    for _, num := range slice {
        uniqueElements[num] = true
    }

    for num := range uniqueElements {
        indices := []int{-1}
        for i, val := range slice {
            if val == num {
                indices = append(indices, i)
            }
        }
        indices = append(indices, len(slice))

        maxBlockSize := 0
        for i := 1; i < len(indices); i++ {
            maxBlockSize = max(maxBlockSize, indices[i]-indices[i-1]-1)
        }

        if maxBlockSize < minMaxBlockSize {
            minMaxBlockSize = maxBlockSize
            minNum = num
        }
    }

    return minNum
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

func main() {
    slice := []int{1, 2, 2, 3, 1, 4, 4, 4, 1, 2, 5}
    fmt.Println(MinimalMaxBlockBruteforce(slice)) // Output: 1
}

This method has a time complexity of O(n^2), as it involves two nested loops: the outer loop cycles through each potential k value and the inner loop sweeps through the slice for each of these k values. However, this approach becomes increasingly inefficient as the size n of the slice grows due to its quadratic time complexity. For larger slices or multiple invocations, the computation time can noticeably increase, demonstrating the need for a more efficient solution.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal