Вопрос о домашней работе массива

Я записал свои мысли о статических классах в более раннем ответе Переполнения стека: Класс с отдельным методом - лучше всего приближаются?

я раньше любил служебные классы, заполненные статическими методами. Они сделали большую консолидацию вспомогательных методов, которые иначе лягут вокруг порождения ад обслуживания и дублирование. Они очень просты в использовании, никакое инстанцирование, никакое распоряжение, просто fire'n'forget. Я предполагаю, что это было моей первой невольной попыткой создания архитектуры для обслуживания широкого круга запросов - много сервисов не сохраняющих состояние, которые просто сделали их задание и ничто иное. Поскольку система растет однако, драконы прибыть.

Полиморфизм

Говорит, что у нас есть метод UtilityClass. SomeMethod, который счастливо шумит вперед. Внезапно мы должны изменить функциональность немного. Большая часть функциональности является тем же, но мы должны изменить несколько частей, тем не менее. Это не был статический метод, мы могли сделать класс производной и изменить содержание метода по мере необходимости. Поскольку это - статический метод, мы не можем. Несомненно, если мы просто должны добавить функциональность или прежде или после старого метода, мы можем создать новый класс и назвать старый в нем - но это просто грубо.

Интерфейсное горе

Статические методы не могут быть определены через интерфейсы по логическим причинам. И так как мы не можем переопределить статические методы, статические классы бесполезны, когда мы должны раздать их их интерфейсом. Это представляет нас неспособный использовать статические классы в качестве части стратегической модели. Мы могли бы исправить некоторые выпуски передающие делегаты вместо интерфейсов .

Тестирование

Это в основном идет рука об руку с интерфейсным упомянутым выше горем. Поскольку наша способность чередующихся реализаций очень ограничена, мы также испытаем затруднения при замене производственного кода тестовым кодом. Снова, мы можем обернуть их, но это потребует, чтобы мы изменили значительные части нашего кода только, чтобы быть в состоянии принять обертки вместо фактических объектов.

Способствует блобам

, Поскольку статические методы обычно используются в качестве служебных методов, и служебные методы обычно будут иметь различные цели, мы быстро закончим с большим классом, заполненным некогерентной функциональностью - идеально, каждый класс должен иметь единственную цель в системе. У меня очень было бы пять временами классы, пока их цели четко определены.

сползание Параметра

Для начала, что мало милого и невинного статического метода могло бы взять единственный параметр. Когда функциональность растет, несколько новых параметров добавляются. Скоро дальнейшие параметры добавляются, которые дополнительные, таким образом, мы создаем перегрузки метода (или просто добавьте значения по умолчанию на языках, которые поддерживают их). В ближайшее время у нас есть метод, который берет 10 параметров. Только первые три действительно требуются, параметры 4-7 дополнительные. Но если параметр 6 определяется, 7-9 требуются, чтобы быть заполненным в также... Мы создали класс с единственной целью сделать то, что сделал этот статический метод, мы могли решить это, беря в обязательных параметрах в конструкторе и позволяя пользователю установить дополнительные значения через свойства или методы для устанавливания нескольких взаимозависимых значений одновременно. Кроме того, если метод вырос до этой суммы сложности, это, скорее всего, должно быть в ее собственном классе так или иначе.

Требовательные потребители для создания экземпляра классов ни по какой причине

Один из наиболее распространенных аргументов: Почему требование, чтобы потребители нашего класса создали экземпляр для вызова этого отдельного метода, не нуждаясь в экземпляре впоследствии? Создание экземпляра класса является очень очень дешевой операцией на большинстве языков, таким образом, скорость не является проблемой. Добавление дополнительной строки кода потребителю является низкой стоимостью для того, чтобы положить начало намного большему удобному в сопровождении решению в будущем. И наконец, если Вы не хотите создавать экземпляры, просто создают одноэлементную обертку Вашего класса, который допускает легкое повторное использование - хотя это действительно делает требование, чтобы Ваш класс был не сохраняющим состояние. Если это не является не сохраняющим состояние, можно все еще создать статические методы обертки, которые обрабатывают все, все еще принося Вам всю пользу в конечном счете. Наконец, Вы могли также сделать класс, который скрывает инстанцирование, как будто это был одиночный элемент: MyWrapper. Экземпляр является свойством, которое просто возвращается new MyClass();

Только соглашение ситхов в абсолютных понятиях

, Конечно, существуют исключения к моей неприязни к статическим методам. Истинные служебные классы, которые не представляют угрозы для чрезмерного увеличения размера, являются превосходными случаями для статических методов - Система. Преобразуйте как пример. Если Ваш проект является одноразовым без требований для будущего обслуживания, полная архитектура действительно не очень важна - статичный или не статичная, действительно не имеет значения - скорость разработки делает, как бы то ни было.

Стандарты, стандарты, стандарты!

Используя методы экземпляра не сдерживает Вас от также использования статических методов, и наоборот. Пока там рассуждает позади дифференцирования, и оно стандартизировано. Нет ничего худшего, чем просмотр бизнес-слоя, растягивающегося с различными методами внедрения.

40
задан Bill the Lizard 15 September 2012 в 02:43
поделиться

9 ответов

Вопрос немного двусмысленный; когда запрос «какой», означает ли это возврат дублируемого значения или позиции в последовательности дублированного? В первом случае подойдет любое из следующих трех решений; если второе, то поможет только первое.

Решение №1: предполагает, что массив неизменяемый

Построить растровое изображение; установите n -й бит при итерации по массиву. Если бит уже установлен, вы нашли дубликат. Он работает с линейным временем и будет работать с массивом любого размера.

Растровое изображение будет создано с таким количеством битов, сколько возможных значений в массиве. Во время итерации по массиву вы проверяете n -й бит в массиве. Если он установлен, вы нашли свой дубликат. Если это не так, установите его. (Логику для этого можно увидеть в псевдокоде этой статьи в Википедии о Битовых массивах или использовать класс System.Collections.BitArray .)

Решение №2: предполагает, что массив является изменяемым

Отсортируйте массив, а затем выполните линейный поиск, пока текущее значение не станет равным предыдущему значению. Использует меньше всего памяти.

33
ответ дан 27 November 2019 в 01:50
поделиться

Подсказка: используйте свойство, которое A XOR A == 0 и 0 XOR A == A.

2
ответ дан 27 November 2019 в 01:50
поделиться

Этот код Python является модификацией QuickSort :

def findDuplicate(arr):
    orig_len = len(arr)
    if orig_len <= 1:
        return None
    pivot = arr.pop(0)
    greater = [i for i in arr if i > pivot]
    lesser = [i for i in arr if i < pivot]
    if len(greater) + len(lesser) != orig_len - 1:
        return pivot
    else:
        return findDuplicate(lesser) or findDuplicate(greater)

Думаю, он находит дубликат в O (n logn)). Он использует дополнительную память в стеке, но я считаю, что его можно переписать, чтобы использовать только одну копию исходных данных:

def findDuplicate(arr):
    orig_len = len(arr)
    if orig_len <= 1:
        return None
    pivot = arr.pop(0)
    greater = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] > pivot]
    lesser = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] < pivot]
    if len(arr):
        return pivot
    else:
        return findDuplicate(lesser) or findDuplicate(greater)

Составления списка, которые производят больше и меньше уничтожают оригинал с вызовами pop (). Если arr не пуст после удаления из него большего и меньшего , то должен быть дубликат, и он должен быть стержнем .

Код страдает от обычных проблем с переполнением стека для отсортированных данных, поэтому необходимо либо случайный поворот, либо итеративное решение, которое ставит данные в очередь:

def findDuplicate(full):
    import copy
    q = [full]
    while len(q):
        arr = copy.copy(q.pop(0))
        orig_len = len(arr)
        if orig_len > 1:
            pivot = arr.pop(0)
            greater = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] > pivot]
            lesser = [arr.pop(i) for i in reversed(range(len(arr))) if arr[i] < pivot]
            if len(arr):
                return pivot
            else:
                q.append(greater)
                q.append(lesser)
    return None

Однако теперь код должен сделать глубокую копию данных в верх петли, изменение требований к памяти.

Вот вам и информатика. Наивный алгоритм сбивает мой код на python, вероятно, из-за алгоритма сортировки python:

def findDuplicate(arr):
    arr = sorted(arr)
    prev = arr.pop(0)
    for element in arr:
        if element == prev:
            return prev
        else:
            prev = element
    return None
6
ответ дан 27 November 2019 в 01:50
поделиться

Если предположить, что все числа от 1 до 1 000 000 находятся в массиве , сумма всех чисел от 1 до 1 000 000 будет (1,000,000) * (1,000,000 + 1) / 2 = 500 000 * 1 000 001 = 500 000 500 000 .

Так что просто сложите все числа в массиве, вычтите 500 000 500 000, и вы получите число, которое встречается дважды.

O (n) раз, и O (1) память.

Если предположение неверно , вы можете попробовать использовать фильтр Блума - они могут храниться намного компактнее, чем хэш-таблица (поскольку они хранят только факт присутствия), но они рискуют получить ложные срабатывания. Однако этот риск может быть ограничен нашим выбором того, сколько памяти потратить на фильтр Блума.

9
ответ дан 27 November 2019 в 01:50
поделиться

А как насчет проблемы поиска ВСЕХ дубликатов? Это можно сделать менее чем за O (n ln n) время? (Сортировка и сканирование) (Если вы хотите восстановить исходный массив, сохраните исходный индекс и измените порядок после конца, что можно сделать за O (n) раз)

0
ответ дан 27 November 2019 в 01:50
поделиться

В качестве варианта решения (2) вы можете использовать radix sort . Никакой дополнительной памяти, и будет работать линейное время. Вы можете утверждать, что время также зависит от размера представления чисел, но вы уже дали для этого границы: радиальная сортировка выполняется за время O (kn), где k - количество цифр, которое вы можете сортировать на каждом проходе. Это делает весь алгоритм O (7n) для сортировки плюс O (n) для проверки дублированного числа - что составляет O (8n) = O (n).

Плюсы:

  • Нет дополнительной памяти
  • O (n)

Минусы:

  • Требуется восемь проходов O (n).
0
ответ дан 27 November 2019 в 01:50
поделиться
def singleton(array):
  return reduce(lambda x,y:x^y, array)
0
ответ дан 27 November 2019 в 01:50
поделиться

Вместо сортировки массива и последующей проверки я бы предложил написать реализацию функции сортировки сравнения, которая завершает как только будет найден дубликат, что приведет к отсутствию дополнительных требований к памяти (очевидно, в зависимости от выбранного вами алгоритма) и времени O (nlogn) в худшем случае (опять же, в зависимости от алгоритма), а не к лучшему (и среднему в зависимости ...) case O (nlogn) time.

Например, реализация сортировки слиянием на месте.

http://en.wikipedia.org/wiki/Merge_sort

2
ответ дан 27 November 2019 в 01:50
поделиться

Сортировка целых чисел путем сортировки их по месту, где они должны быть. Если вы получили "коллизию", значит, вы нашли правильный номер.

сложность пространства O (1) (точно такое же пространство, которое можно перезаписать) временная сложность меньше, чем O (n), потому что вы статистически обнаружите столкновение, прежде чем попадете в конец.

0
ответ дан 27 November 2019 в 01:50
поделиться