🗺️

Go言語のmapについて、問題を眺めながら超基礎部分だけをまとめる

2024/11/11に公開

問題

整数のスライスを引数に取り、スライスの中で最も頻繁に現れる要素を返す関数をGoで実装してください。同じ頻度の要素が複数ある場合は、その中のどれか一つを返してください。

  • 入力: [1, 2, 2, 3, 3, 3, 4] の場合、出力は 3
  • 入力: [5, 5, 6, 6, 7, 7, 7, 6] の場合、出力は 6 か 7 (どちらでも正解)

Go言語のmapとは?

Goの map は、 キーと値のペア を保持するデータ構造です。キーを使って値にアクセスしたり、キーに対応する値を変更したり削除したりすることができます。

基本的な構文

var m map[KeyType]ValueType
  • KeyType: キーのデータ型(例:int, string, float64 など)。
  • ValueType: 値のデータ型(例:int, string, bool など)。

たとえば、map[int]string はキーが int で、値が string のマップを意味します。

mapの作成方法

  1. make関数で作成

    m := make(map[int]string)
    
  2. リテラルを使って作成

    m := map[int]string{
        1: "One",
        2: "Two",
        3: "Three",
    }
    

要素の追加・アクセス・削除

1. 要素の追加

mapに要素を追加するには、キーを指定して値を代入します。

m := make(map[int]string)
m[1] = "One"  // キー1に値"One"を追加
m[2] = "Two"  // キー2に値"Two"を追加

2. 要素の取得

キーを使って要素にアクセスします。

value := m[1]  // "One" を取得

3. 要素の削除

delete 関数を使って、特定のキーの要素を削除します。

delete(m, 1)  // キー1の要素を削除

4. キーの存在確認

Goでは、キーが存在するかどうかを確認するために、2つの戻り値を使うことができます。

value, exists := m[1]
if exists {
    fmt.Println("キー1が存在します:", value)
} else {
    fmt.Println("キー1は存在しません")
}

mapの便利な使い方

  1. ループで全要素を取得
    range を使って、map の全要素に対してループを回すことができます。

    for key, value := range m {
        fmt.Println(key, value)
    }
    

    このようにして、全てのキーとその対応する値を処理できます。

  2. mapの長さを取得
    len 関数で map の要素数を取得できます。

    fmt.Println(len(m))  // mapに入っているキーと値のペアの数を返す
    

mapの使用例

以下は、整数をキーとして、その平方数を値として保存するmapの例です。

package main

import "fmt"

func main() {
    squares := make(map[int]int)

    // キーと値の追加
    squares[1] = 1
    squares[2] = 4
    squares[3] = 9

    // 値の取得
    fmt.Println("2の平方:", squares[2])  // 4が出力される

    // mapのループ
    for key, value := range squares {
        fmt.Printf("キー: %d, 値: %d\n", key, value)
    }

    // 要素の削除
    delete(squares, 2)

    // 削除後の確認
    if _, exists := squares[2]; !exists {
        fmt.Println("キー2は存在しません")
    }
}

問題を解いてみる

では、先ほどの問題(最も頻繁に現れる要素を見つける問題)の解説を行います。

package main

import "fmt"

func mostFrequent(num_slice []int) int {
    // 出現回数を保存するマップを作成
    frequencyMap := make(map[int]int)

    // スライス内の要素の出現回数を数える
    for _, num := range num_slice {
        frequencyMap[num]++  // 既にkeyが存在していればvalueを+1、存在していなければ新たにkeyを作成しvalueを1にする
    }

    // 最も頻繁に出現する要素を見つける
    mostFrequentNum := num_slice[0] // 最も頻繁に出現する要素
    maxCount := 0                   // 最も頻繁に出現する要素の出現回数

    // マップをループして最大の出現回数を持つ要素を探す
    for num, count := range frequencyMap {

        // 出現回数が最大の要素を更新
        if count > maxCount {
            mostFrequentNum = num // 最も頻繁に出現する要素を更新
            maxCount = count       // 最も頻繁に出現する要素の出現回数を更新
        }
    }

    return mostFrequentNum
}

func main() {
    fmt.Println(mostFrequent([]int{1, 2, 2, 3, 3, 3, 4}))  // 3が出力される
    fmt.Println(mostFrequent([]int{5, 5, 6, 6, 7, 7, 7, 6}))  // 6または7が出力される
}

解説

1. マップの作成

frequencyMap := make(map[int]int)
  • frequencyMap は整数をキーに、出現回数を値に持つマップです。このマップを使って、スライス内の各要素が何回出現したかを記録します。

2. 出現回数を数える

for _, num := range num_slice {
    frequencyMap[num]++
}
  • スライス num_slice の各要素 num をループで取り出し、frequencyMap[num] に対応する値をインクリメント(1増やす)します。これで各要素の出現回数を記録します。

3. 最も頻繁に出現する要素を見つける

mostFrequentNum := num_slice[0]
maxCount := 0

for num, count := range frequencyMap {
    if count > maxCount {
        mostFrequentNum = num
        maxCount = count
    }
}
  • 最初に mostFrequentNum にスライスの最初の要素を仮の最頻出要素として設定し、maxCount には0を設定します。
  • 次に、マップ frequencyMap をループして各要素の出現回数をチェックし、maxCount より大きい出現回数があれば、その要素を新しい mostFrequentNum に更新します。

4. 結果の返却

return mostFrequentNum
  • ループが終わった後に、mostFrequentNum には最も頻繁に出現する要素が格納されているので、それを返します。

ポイント

  • mapの使い方: map を使って、要素の出現回数を効率的に記録し、後で最頻出要素を探すために利用しています。
  • ループ処理: 最初にスライス内の各要素の出現回数を数え、その後に最大の出現回数を持つ要素を探す2段階のループ処理です。

このように、map を使うことで効率的に要素の出現回数を管理し、最頻出要素を見つけることができます。

Discussion