Мажорирующий элемент — различия между версиями
| Никита (обсуждение | вклад)  (→Псевдокод) | Никита (обсуждение | вклад)   (→Псевдокод) | ||
| Строка 49: | Строка 49: | ||
|      // candidates - словарь, где ключ - стоящий элемент, |      // candidates - словарь, где ключ - стоящий элемент, | ||
|      // значение - количество таких стоящих элементов |      // значение - количество таких стоящих элементов | ||
| − |      for i = 0 to N - 1 | + |      '''for''' i = 0 '''to''' N - 1 | 
| − |        if candidates.containsKey(a[i]) // нашли стоящий элемент | + |        '''if''' candidates.containsKey(a[i]) // нашли стоящий элемент | 
|          candidates[a[i]]++ // увеличим счетчик |          candidates[a[i]]++ // увеличим счетчик | ||
| − |        else | + |        '''else''' | 
| − |          if candidates.size() < K - 1 // полная группа не образована | + |          '''if''' candidates.size() < K - 1 // полная группа не образована | 
|            candidates[a[i]] = 1 // добавим элемент в группу |            candidates[a[i]] = 1 // добавим элемент в группу | ||
| − |          else // образовалась полная группа | + |          '''else''' // образовалась полная группа | 
| − |            for element in candidates // посадим группу | + |            '''for''' element '''in''' candidates // посадим группу | 
|              candidates[element]-- |              candidates[element]-- | ||
| − |              if candidates[element] == 0 // если никто с таким элементом не стоит | + |              '''if''' candidates[element] == 0 // если никто с таким элементом не стоит | 
|                candidates.remove(element) // удалим этот элемент |                candidates.remove(element) // удалим этот элемент | ||
| − |      for candidate in candidates // проверим, встречается ли элемент N / K раз | + |      '''for''' candidate '''in''' candidates // проверим, встречается ли элемент N / K раз | 
|        count = 0 |        count = 0 | ||
| − |        for i = 0 to N - 1 | + |        '''for''' i = 0 '''to''' N - 1 | 
| − |          if a[i] == candidate | + |          '''if''' a[i] == candidate | 
|            count++ |            count++ | ||
| − |        if count > N / K | + |        '''if''' count > N / K | 
|          elements.add(element) |          elements.add(element) | ||
| − |      return candidates | + |      '''return''' candidates | 
| === Доказательство === | === Доказательство === | ||
Версия 03:12, 26 мая 2013
Содержание
Формулировка задачи
Требуется в массиве длиной найти элемент, встречающийся более раз. Гарантируется, что такой элемент существует.
Решение за O(N)
Алгоритм можно представить следующим образом: пусть на вечеринке собрались людей, и каждому из них соответствует один элемент из массива. Когда встречаются двое с разными элементами, то они образуют пару и садятся. В конце концов останутся стоять только гости с одинаковыми элементами. Это и есть искомый элемент.
Будем идти по массиву и запоминать элемент, для которого еще не нашлось пары. При встрече такого же элемента увеличиваем счетчик "без пары", иначе - уменьшаем. Если все элементы уже имеют пару, то говорим, что у текущего элемента пары нет.
Псевдокод
 findMajorityElement(a, N)
   count = 0 // количество людей, оставшихся стоять
   candidate = 
   
   for i = 0 to N - 1
     if count == 0 // никто не стоит
       candidate = a[i] // встанет текущий элемент
       count++ // увеличим количество стоящих
     else // кто-то стоит
       if a[i] == candidate // стоит такой же элемент
         count++ // увеличим количество стоящих
       else // стоит другой элемент => подобрали пару
         count-- // уменьшим количество стоящих
    
    return candidate
Доказательство
На ом шаге выполняется следующий инвариант: если , то - мажорирующий элемент на подмассиве , либо мажорирующего элемента на данном подмассиве не существует. Тогда на N-ом шаге будет содержать мажорирующий элемент на всем массиве, т.к. гарантируется его существование. Покажем, что данный инвариант всегда выполняется.
Пусть данный инвариант выполняется на ом шаге. Тогда на ом шаге возможны 3 варианта:
-  Очевидно, что на подмассиве мажорирующего элемента не существует, так как все элементы разбились на пары. Тогда только может быть мажорирующим элементом. 
-   и Если на подмассиве существует мажорирующий элемент, то он находится в . Тогда, в силу равенства и , если на подмассиве существует мажорирующий элемент, то он тоже будет равен . 
-   и Если на подмассиве существует мажорирующий элемент, то он находится в . Тогда, в силу неравенства и , образовалась новая пара. Если не станет равным нулю, то опять же мажорирующим элементом может быть только candidate, так как для всех остальных мы нашли пару, а значит встречаются они не более раз. 
Всего происходит  итераций, каждая из которых обрабатывается за . Итоговая асимптотика .
Обобщение на случай поиска элемента, встречающегося N/K раз
Будем пользоваться той же идеей, что и в предыдущем пункте. До этого мы садили людей парами, а теперь будем садить группами из человек. В итоге, если искомые нами элементы есть, то они останутся стоять.
Будем идти по массиву и хранить элементы, которые еще не сели. При встрече элемента, который уже есть среди стоящих, увеличиваем счетчик данного элемента на . В противном случае смотрим, можем ли мы посадить группу и, либо ее садим, либо добавляем текущий элемент к стоящим. В конце требуется сделать проверку, что оставшиеся стоять элементы встречаются раз.
Псевдокод
 findMajorityElement(a, N, K)
   // candidates - словарь, где ключ - стоящий элемент,
   // значение - количество таких стоящих элементов
   for i = 0 to N - 1
     if candidates.containsKey(a[i]) // нашли стоящий элемент
       candidates[a[i]]++ // увеличим счетчик
     else
       if candidates.size() < K - 1 // полная группа не образована
         candidates[a[i]] = 1 // добавим элемент в группу
       else // образовалась полная группа
         for element in candidates // посадим группу
           candidates[element]--
           if candidates[element] == 0 // если никто с таким элементом не стоит
             candidates.remove(element) // удалим этот элемент
   
   for candidate in candidates // проверим, встречается ли элемент N / K раз
     count = 0
     for i = 0 to N - 1
       if a[i] == candidate
         count++
     if count > N / K
       elements.add(element)
     
   return candidates
Доказательство
Альтернативное решение
Выберем случайный элемент в массиве и проверим, встречается ли он больше, чем раз. Будем делать так, пока не найдем подходящий элемент. Утверждается, что данный алгоритм в среднем работает за
Псевдокод
findMajorityElement(a, N, K)
  while true
    candidate = a[random(N)]
    count = 0
    for i = 0 to N - 1
      if a[i] == candidate
        count++
    if count > N / K
      return candidate
Доказательство
На каждом шаге мы берем случайный элемент. Проверка на мажорируемость выполняется за . Вероятность, что мы выбрали элемент "удачно" составляет . Тогда, в среднем, мы будем делать проверок. Итоговая сложность .
