Найти доминирующий елемент в масиве (алгоритм)
Есть одномерный масив. Извесно что в нем есть доминирующий елемент (тоесть елемент который встречается больше чем в половине ячеек масива). Нужно его найти за минимальную сложность.
Пример: Масив - 1 2 2 2 3 2 Ответ - 2 (потому что 2 встеречаеется более 3-х раз).
Спасло тогда то, что на элементы массива стояло ограничение от 1 до 1000. Тогда я просто объявил один большой разряженный массив от 1 до 1000 и по мере считывания из файла чисел элемент этого массива с индексом равным считанному числу увеличивал на 1 и запоминал этот индекс, если полученное после увеличения значение было больше уже существующего максимального.
Бред конечно. :) Но все тесты решение прошло на ура. Хорошо что на олимпиадах исходники не смотрят.
Изучив через пару недель класс std::map, понял, что тоже самое можно было сделать с его помощью. Тогда бы решение было не столь требовательно к памяти, да и большие ограничения были бы не помеха, хотя и чуть медленее.
Интересная подробность :)
Тогда можно:
1. отсортировать массив,
2. считать число цифр до перехода от одной цифры к другой,
3. если количество меньше половины всех цифр - вернуться к п.2
4. выдать результат - число, количество которых больше чем половина количества всех цифр.
Примерно так :)
1. отсортировать массив,
2. считать число цифр до перехода от одной цифры к другой,
3. если количество меньше половины всех цифр - вернуться к п.2
4. выдать результат - число, количество которых больше чем половина количества всех цифр.
Не самый оптимальный вариант, но вполне жызнеспособный.
Тут вся нагрузка пойдет на сортирование. Получим N log(N) (хотя можно и поспорить но в общем случае будет такая сложность ).
Относительно пунктов 2 - 4. Их можно сильно соптимизировать. Оч сильно. :)
ЗЫ. А вообще не факт что масив получится отсортировать. По большому щету операции <, > могут быть неопределены для елементов масива, но пока пусть елементами будут целые числа.
- Есть алгоритм поиска порядковой статистике. В данном случии медианы.
http://en.wikipedia.org/wiki/BFPRT
Пословам это быстрее сортировки.
Пословам это быстрее сортировки.
К сожалению в статистике я полный 0 :(.
Но уверен что оптимальный алгоритм быстрее.
Ну, можно просуммировать по половинам и поделить на количество половины. Потом поискать пару чисел в каждой из половин, которые с результатом совпадут.
А вообще... Смутно представляется алгоритм в котором сравниваем все числа с соседним, а последнее с первым. Если нет равенства, то убираем число из массива, из следующей итерации сравнений. В каждой итерации смотрим были ли убраны числа. Когда перестанут убираться - останутся только доминирующие...
да. Туплю :)
Ето той порядковой статистикой ? Сори за тупой вопрос. Я просто не успел еще в нее посмотреть толком.
Ыыы.... Туплю с утра. Можно подробнее ?
Черновой (и не отлаженный) вариант пока таков:
{
int[] source =
//{ 2, 8, 2, 8, 2, 8, 2 };
//{ 2, 2, 2, 8, 8, 8, 2 };
{ 2, 2, 2, 8, 8 };
List<int> list = new List<int>();
list.AddRange(source);
while (list.Count > 1)
{
ProcessItemPairs(list);
}
if (list.Count == 0)
Console.WriteLine("Нет решения");
else
Console.WriteLine(list[0]);
}
static void ProcessItemPairs(List<int> values)
{
for (int i = values.Count - 2; i >= 0; i -=2 )
{
int j = i + 1;
if (values == values[j])
values.RemoveAt(j);
else
{
values.RemoveAt(j);
values.RemoveAt(i);
}
}
}
Похоже на правду.
Я не запускал, но кажись входы
8 8 2 2 2
8 8 2 2 2 2
не пройдут ??
ЗЫ и сложность тут будет не N/2 а N (Точнее N-1 в худшем случае)
Я не запускал, но кажись входы
8 8 2 2 2
8 8 2 2 2 2
не пройдут ??
ЗЫ и сложность тут будет не N/2 а N (Точнее N-1 в худшем случае)
Так какое же правильное решение?
8 8 2 2 2
8 8 2 2 2 2[/QUOTE]Есть такое, надо учитывать чётность количества элементов. Остаточный, необработанный элемент выходит на следующий уровень с "удвоенной мощностью". Надо доработать :)
Ну не надо говорить! Rebbit, скажи только его сложность :)
Я лутше о твоей сложности скажу.
С одной стороны ти убираеш елементы с масива. Ето уже бет по сложности. Просто мы етого не видем. Можно сказать что мы будем использовать не масив а связный список. Хорошо. Но как тогда к нему быстро обращаться по индексу ?
Хотя тоже можно, но придется делать спецыфическую структуру данных, которая будет требовать переиндексацыи. :)
ЗЫ. Если чуток поменять реализацыю и не обращаться к елементам по индексам, то можно розрулить ету проблему.
Самое первое решение, которое напрашивается - это сортровка, а сл-но время N log(N).
Но можно подумать вот над чем.
Доминирующее элемент - это каждый второй елемент, т.к. встречается он в полее половине ячеек. Если эту "вторую" ячейку занял недоминирующий элемент, значит, где-то будут идти два доминирующих элемента подрят.
Думаю, можно как-то развить далее в этом направлении.
Я бы даже сказал - каждый нечетный
тут без разницы, четный он или нет (ИМХО)
просто в "худшем" (по разумению моего алгоритма) случае - когда число эл-тов нечетное, а число доминант всего на 1 больше числа остальных эл-тов получится массив вида 1,2,1,2,1,2,1,2,1,2,1. Вот я про эти нечетные и говорю.
Не вижу разницы. Скажем, на массиве вида aaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccaaabbcba доминирующий элемент - чётный в первом вхождении. :)
Как уже заметил Phodopus - не всегда. Да и что нам ето даст ? Недоминирующие тоже могут идти подряд.
Вот-вот-вот.. Кроме единственного случая который я и привел, где "соседств" нет. И не то что бы количество соседств - но их общая значимость. Тоесть когда aabbbbbbaaabbbbbbbaa - "соседств" для "a"-то больше чем для доминирующего "b".
Верно. Это уже реализовано, но я ищу пары в цикле, а надо рекурсией. Останется та самая первая/последняя "единичка".
Ну, и заменить RemoveAt на вычисление индексов сравниваемых элементов.
Мой код и отражает суть.
Неправильно здесь:
ProcessItemPairs(list);
Ну, и заменить RemoveAt на вычисление индексов сравниваемых элементов.
Как только я первый раз прочитал эту тему - "бросился" решать рекурсией. Потом для себя решил что это доп. память, а значит, читерство :). Шучу на самом деле, но все-таки от рекурсии решил отказаться (да и алгоритм придумал неверный). В итоге сделал итеративно. Алгоритм послал Rebbit через ЛС, но он сказал что его решение куда проще. А выкладывать мой алгоритм сюда или нет - пусть он решает - считаю надо чтоб все-таки интрига оставалась :), но если хотите - пожалуйста (тем более я не уверен насчет его абсолютной корректности).
Реализация:
{
vector<int> p(sz), pa(a, a + sz);
while(true)
{
int m = 0;
for(int i = 0; i < sz; ++i)
{
int j = i + 1;
if (j >= sz) j = 0;
if(pa == pa[j])
{
p[m] = pa;
++m;
}
}
if(sz == m) break;
sz = m;
pa = p;
}
int result = p[0];
return result;
}
int main()
{
int mass[11] = {1,1,1,1,1,1,2,2,2,2,2};
cout << testArray(mass, 11) << endl;
return 0;
}
Насколько я понимаю - от N до N*(1 + N/2).
Тестируйте :)
2Phodopus выкладывай сюда если хочеш.
el: Integer;
cnt: Integer;
end;
var
Sq: array [0..1] of TSeq;
P: PIntegerArray;
h, i, j, k, Count, SqIdx, MinElDom, DomEl, T: Integer;
begin
Sq[0].el := P[0];
Sq[0].cnt := 1;
Sq[1].el := 0;
Sq[1].cnt := 0;
SqIdx := 0;
for i := 1 to Count - 1 do
if P <> Sq[SqIdx].el then
begin { переход }
if Sq[1 - SqIdx].el = P then
begin
SqIdx := 1 - SqIdx;
Inc(Sq[SqIdx].cnt);
end
else begin
if Sq[0].cnt > Sq[1].cnt then
SqIdx := 1 { затираем где меньше послед. эл-тов насчиталось }
else
if Sq[0].cnt < Sq[1].cnt then
SqIdx := 0 { затираем где меньше послед. эл-тов насчиталось }
else
SqIdx := 1 - SqIdx; { затираем не последнюю использованную }
Sq[SqIdx].el := P;
Sq[SqIdx].cnt := 1;
end;
end
else
Inc(Sq[SqIdx].cnt);
if Sq[0].cnt > Sq[1].cnt then
ShowMessage(Format('el:%d', [Sq[0].el]))
else
if Sq[0].cnt < Sq[1].cnt then
ShowMessage(Format('el:%d', [Sq[1].el]))
else
ShowMessage(Format('el:%d', [P[0].el]))
вот что породил мой воспаленный мосх :). Посмотрите, может есть тест-кейс на котором он и обломается? А то доказывать верность алгоритма не мой конек..
ПыСы. Естественно для массивов с 3 и более элементами
ПыСы. Естественно для массивов с 3 и более элементами
Алгоритм почти понравился. Компилятора Паскаля нет, на си++ переводить лень :)
2222111102220
Если правильно понял алгоритм, то первый и последний ноль затрет двойку и результат будет 1.
Мда :). Вечером попробую выкидывать последовательность только если новая найденная - длинее. Была и такая мысль.
Sq0cnt := 1;
Sq1el := 0;
Sq1cnt := 0;
SqIdx := 0;
i := 1;
while (P = Sq0el) and (i < Count) do
begin
Inc(i);
Inc(Sq0cnt);
end;
if i < Count then
begin
Sq1el := P;
Sq1cnt := 0;
for i := i to Count - 1 do
if P <> Sq1el then
begin
if Sq1cnt > Sq0cnt then
begin
Sq0el := Sq1el;
Sq0cnt := Sq1cnt;
end;
Sq1el := P;
Sq1cnt := 1;
end
else
Inc(Sq1cnt);
end;
ShowMessage(Format('el:%d', [Sq0el]));
Как-то так, но счаз уже мосх не работает, поэтому могут быть бааааальшие элементарные ляпы..
:mad: чушь написал. На первом же тест-кейсе к предыдущему алгоритму свалился. Но все-равно оставлю здесь.