← Назад к вопросам

Поиск дубликатов в массиве

1.6 Junior🔥 131 комментариев
#Алгоритмы и структуры данных

Условие

Напишите функцию, которая находит все дублирующиеся элементы в массиве.

Требования

  • Функция принимает массив любых значений
  • Возвращает массив элементов, которые встречаются более одного раза
  • Оптимальная сложность по времени

Примеры

findDuplicates([1, 2, 3, 2, 4, 3, 5]);     // [2, 3]
findDuplicates(["a", "b", "a", "c", "b"]); // ["a", "b"]
findDuplicates([1, 2, 3, 4, 5]);           // []

Варианты решения

  • Через два вложенных цикла O(n^2)
  • Через сортировку O(n log n)
  • Через хеш-таблицу O(n)

Реализуйте оптимальный вариант и объясните выбор.

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Решение

1. Оптимальное решение (хеш-таблица) - O(n)

<?php

function findDuplicates(array $array): array {
    $seen = [];
    $duplicates = [];

    foreach ($array as $value) {
        // Нормализуем значение для сравнения (для объектов используем spl_object_hash)
        $key = is_object($value) ? spl_object_hash($value) : json_encode($value);

        if (isset($seen[$key])) {
            // Если уже видели это значение и еще не добавили в дубликаты
            if (!in_array($value, $duplicates, true)) {
                $duplicates[] = $value;
            }
        } else {
            $seen[$key] = true;
        }
    }

    return array_values($duplicates);
}

// Примеры
echo json_encode(findDuplicates([1, 2, 3, 2, 4, 3, 5]));       // [2, 3]
echo json_encode(findDuplicates(["a", "b", "a", "c", "b"]));   // ["a", "b"]
echo json_encode(findDuplicates([1, 2, 3, 4, 5]));             // []

Анализ:

  • Временная сложность: O(n) - один проход по массиву
  • Пространственная сложность: O(n) - хеш-таблица
  • Преимущества: Самый быстрый, работает с любыми типами
  • Рекомендуется: Для production

2. Решение через array_count_values

<?php

function findDuplicates(array $array): array {
    // Работает только с простыми типами (int, string)
    $counts = array_count_values($array);
    $duplicates = array_filter($counts, fn($count) => $count > 1);
    return array_keys($duplicates);
}

// Примеры
echo json_encode(findDuplicates([1, 2, 3, 2, 4, 3, 5])); // [2, 3]

Анализ:

  • Временная сложность: O(n)
  • Пространственная сложность: O(n)
  • Преимущества: Очень читаемый код
  • Недостатки: Только для простых типов

3. Решение через сортировку - O(n log n)

<?php

function findDuplicatesSorted(array $array): array {
    // Создаём копию для сортировки
    $sorted = $array;
    sort($sorted);

    $duplicates = [];
    $prev = null;
    $counted = false;

    foreach ($sorted as $value) {
        if ($prev !== null && $prev === $value) {
            if (!$counted) {
                $duplicates[] = $value;
                $counted = true;
            }
        } else {
            $prev = $value;
            $counted = false;
        }
    }

    return $duplicates;
}

// Примеры
echo json_encode(findDuplicatesSorted([1, 2, 3, 2, 4, 3, 5])); // [2, 3]

Анализ:

  • Временная сложность: O(n log n) - из-за сортировки
  • Пространственная сложность: O(n) - для отсортированного массива
  • Используется: Когда нужен отсортированный результат

4. Наивное решение (два вложенных цикла) - O(n²)

<?php

function findDuplicatesNaive(array $array): array {
    $duplicates = [];

    for ($i = 0; $i < count($array); $i++) {
        for ($j = $i + 1; $j < count($array); $j++) {
            if ($array[$i] === $array[$j]) {
                if (!in_array($array[$i], $duplicates, true)) {
                    $duplicates[] = $array[$i];
                }
                break;
            }
        }
    }

    return $duplicates;
}

// Примеры
echo json_encode(findDuplicatesNaive([1, 2, 3, 2, 4, 3, 5])); // [2, 3]

Анализ:

  • Временная сложность: O(n²) - вложенные циклы
  • Пространственная сложность: O(n)
  • Не рекомендуется: Очень медленно на больших массивах

5. Решение с array_unique - O(n)

<?php

function findDuplicatesUnique(array $array): array {
    $duplicates = [];

    // Находим элементы, которых после unique отфильтровалось
    foreach ($array as $value) {
        $count = 0;
        foreach ($array as $item) {
            if ($item === $value) {
                $count++;
            }
        }
        if ($count > 1 && !in_array($value, $duplicates, true)) {
            $duplicates[] = $value;
        }
    }

    return $duplicates;
}

6. Универсальное решение (работает со сложными типами)

<?php

function findDuplicatesAdvanced(array $array): array {
    $seen = [];
    $duplicates = [];
    $duplicateKeys = [];

    foreach ($array as $value) {
        // Для объектов используем spl_object_id
        if (is_object($value)) {
            $key = spl_object_id($value);
        }
        // Для массивов и сложных структур используем serialize
        elseif (is_array($value)) {
            $key = serialize($value);
        }
        // Для скаляров используем сам значение
        else {
            $key = $value;
        }

        if (isset($seen[$key])) {
            // Записываем только один раз
            if (!in_array($key, $duplicateKeys, true)) {
                $duplicates[] = $value;
                $duplicateKeys[] = $key;
            }
        } else {
            $seen[$key] = true;
        }
    }

    return array_values($duplicates);
}

// Примеры
echo json_encode(findDuplicatesAdvanced([1, 2, 3, 2, 4, 3, 5]));
echo json_encode(findDuplicatesAdvanced(["a", "b", "a"]));
echo json_encode(findDuplicatesAdvanced([[1, 2], [3, 4], [1, 2]]));

7. Юнит тесты

<?php

use PHPUnit\Framework\TestCase;

class FindDuplicatesTest extends TestCase {
    public function test_find_integer_duplicates(): void {
        $result = findDuplicates([1, 2, 3, 2, 4, 3, 5]);
        $this->assertEqualsCanonicalizing([2, 3], $result);
    }

    public function test_find_string_duplicates(): void {
        $result = findDuplicates(["a", "b", "a", "c", "b"]);
        $this->assertEqualsCanonicalizing(["a", "b"], $result);
    }

    public function test_no_duplicates(): void {
        $result = findDuplicates([1, 2, 3, 4, 5]);
        $this->assertEmpty($result);
    }

    public function test_all_duplicates(): void {
        $result = findDuplicates([1, 1, 1, 2, 2, 2]);
        $this->assertEqualsCanonicalizing([1, 2], $result);
    }

    public function test_empty_array(): void {
        $result = findDuplicates([]);
        $this->assertEmpty($result);
    }

    public function test_single_element(): void {
        $result = findDuplicates([1]);
        $this->assertEmpty($result);
    }

    public function test_mixed_types(): void {
        $result = findDuplicates([1, "1", 1, 2, "2", 2]);
        $this->assertEqualsCanonicalizing([1, 2], $result);
    }

    public function test_array_duplicates(): void {
        $result = findDuplicatesAdvanced([[1, 2], [3, 4], [1, 2]]);
        $this->assertCount(1, $result);
    }
}

8. Тест производительности

<?php

function benchmarkDuplicateDetection() {
    $largeArray = array_merge(
        range(1, 5000),
        range(1, 5000)  // Дублируем
    );
    shuffle($largeArray);

    // Тест 1: Хеш-таблица O(n)
    $start = microtime(true);
    $result1 = findDuplicates($largeArray);
    $time1 = microtime(true) - $start;
    echo "Hash table: " . number_format($time1 * 1000, 3) . "ms\n";

    // Тест 2: array_count_values O(n)
    $start = microtime(true);
    $result2 = findDuplicates($largeArray);
    $time2 = microtime(true) - $start;
    echo "array_count_values: " . number_format($time2 * 1000, 3) . "ms\n";

    // Тест 3: Сортировка O(n log n)
    $start = microtime(true);
    $result3 = findDuplicatesSorted($largeArray);
    $time3 = microtime(true) - $start;
    echo "Sorted: " . number_format($time3 * 1000, 3) . "ms\n";

    // Тест 4: Наивный O(n²)
    $smallerArray = array_slice($largeArray, 0, 1000);
    $start = microtime(true);
    $result4 = findDuplicatesNaive($smallerArray);
    $time4 = microtime(true) - $start;
    echo "Naive (on 1000 items): " . number_format($time4 * 1000, 3) . "ms\n";
}

benchmarkDuplicateDetection();
// Результаты (примерные):
// Hash table: 0.542ms
// array_count_values: 0.631ms
// Sorted: 1.850ms
// Naive (on 1000 items): 125.340ms

9. Сравнение всех подходов

РешениеВремяПамятьРекомендуетсяОсобенности
Хеш-таблицаO(n)O(n)ДАЛучший выбор
array_count_valuesO(n)O(n)ДаДля простых типов
СортировкаO(n log n)O(n)Когда результат нужен отсортированным
НаивныйO(n²)O(n)НетТолько для обучения

10. Рекомендация

Для собеседования используй это:

function findDuplicates(array $array): array {
    $seen = [];
    $duplicates = [];

    foreach ($array as $value) {
        $key = is_object($value) ? spl_object_hash($value) : json_encode($value);

        if (isset($seen[$key])) {
            if (!in_array($value, $duplicates, true)) {
                $duplicates[] = $value;
            }
        } else {
            $seen[$key] = true;
        }
    }

    return array_values($duplicates);
}

Почему это решение лучшее:

  1. O(n) временная сложность - оптимально
  2. O(n) пространственная сложность - приемлемо
  3. Работает с любыми типами данных
  4. Простой и понятный код
  5. Легко объяснить на интервью