配列内の重複要素を特定するアルゴリズム

以下は、C++における配列サイズの取得に関するコード例である。関数に渡された配列はポインタに変換されるため、sizeof演算子は元の配列サイズではなくポインタのサイズ(通常4または8バイト)を返す点に注意が必要である。


#include <cstdio>

size_t get_array_size(int arr[]) {
    return sizeof(arr); // 実際にはポインタのサイズが返される
}

int main() {
    int data1[] = {1, 2, 3, 4, 5};
    size_t size1 = sizeof(data1);       // 配列全体のバイト数(例: 20)

    int* data2 = data1;
    size_t size2 = sizeof(data2);       // ポインタのサイズ(例: 8)

    size_t size3 = get_array_size(data1); // 同様にポインタのサイズ

    printf("%zu, %zu, %zu\n", size1, size2, size3);
    return 0;
}

問題1:配列を変更して重複値を検出

長さ n の整数配列が与えられ、各要素は 0 から n-1 の範囲にある。このとき、任意の重複要素を1つ見つけるアルゴリズムを実装せよ。配列を直接変更してもよい。


bool find_duplicate_inplace(int arr[], int n, int* result) {
    if (!arr || n <= 0) return false;

    for (int i = 0; i < n; ++i) {
        if (arr[i] < 0 || arr[i] >= n) return false;
    }

    for (int i = 0; i < n; ++i) {
        while (arr[i] != i) {
            if (arr[arr[i]] == arr[i]) {
                *result = arr[i];
                return true;
            }
            std::swap(arr[i], arr[arr[i]]);
        }
    }
    return false;
}

この手法は、各値をその値に対応するインデックス位置に配置することで動作する。もしすでに正しい位置に同じ値が存在すれば、それが重複である。時間計算量は O(n)、空間計算量は O(1) である。

問題2:配列を変更せずに重複値を検出

長さ n+1 の配列が与えられ、各要素は 1 から n の範囲にある。配列を一切変更せずに、任意の重複要素を1つ見つけるアルゴリズムを実装せよ。


#include <iostream>

int find_duplicate_readonly(const int arr[], int len) {
    if (!arr || len <= 1) return -1;

    int low = 1;
    int high = len - 1;

    while (low < high) {
        int mid = low + (high - low) / 2;
        int count = 0;

        for (int i = 0; i < len; ++i) {
            if (arr[i] >= low && arr[i] <= mid) {
                ++count;
            }
        }

        if (count > mid - low + 1) {
            high = mid;
        } else {
            low = mid + 1;
        }
    }
    return low;
}

int main() {
    const int nums[] = {6, 6, 5, 4, 3, 2, 6, 7};
    int dup = find_duplicate_readonly(nums, 8);
    std::cout << dup << std::endl; // 出力: 6
    return 0;
}

この解法は二分探索に基づく。数値の範囲 [1, n] を分割し、各区間内に含まれる要素数をカウントする。もしカウントが区間の幅を超えていれば、その区間に重複が存在すると判断する。時間計算量は O(n log n)、空間計算量は O(1) である。ただし、すべての重複値を検出できるとは限らない。

タグ: C++ Array duplicate-detection Algorithm binary-search

7月18日 21:11 投稿