Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Разработка эффективных алгоритмов.doc
Скачиваний:
220
Добавлен:
24.11.2019
Размер:
1.2 Mб
Скачать

2.3. Задача выборки

Иногда нам нужен элемент из списка, обладающий некоторыми специальными свойствами, а не имеющий некоторое конкретное значение.

Например, вместо записи с конкретным значением поля нас интересует запись с наибольшим, или наименьшим, или средним значением этого поля.

В общем случае нас может интересовать запись с k-м по величине значением поля.

Один из способов найти такую запись состоит в том, чтобы отсортировать список в порядке убывания по этому полю; тогда запись с k-м по величине значением окажется на k-м месте. На это уйдет гораздо больше сил (и времени), чем необходимо: значения, меньше искомого, нас не интересуют.

Может пригодиться следующий подход: мы находим наибольшее значение в списке и помещаем его в конец. Затем мы можем найти наибольшее значение в оставшейся части списка, исключая уже найденное.

В результате мы получаем второе по величине значение списка, которое можем поместить на второе с конца место в списке. Повторив эту процедуру k раз, мы найдем k-тое по величине значение (мы завершаем сортировку раньше, а, именно, через k шагов)

FindKmax (list, k, N)

// list – список для просмотра

// k – порядковый номер требуемого элемента

// N – размер списка

for i=1 to k do

max  list [1]

nom  1 // номер максимального элемента

for j = 2 to N – (i-1) do

if max < list (j) then

max  list [j]

nom  j

endif

endfor

Swap (list [N – (i – 1)], list [nom])

endfor

return (max)

end

Какова сложность этого алгоритма?

На каждом проходе мы присваиваем переменной max первое значение из списка, и далее сравниваем эту переменную со всеми остальными элементами.

Для первого прохода будет выполнено (N-1) сравнение, для 2-го – (N-2) … для k-того – (N-k), поэтому всего для поиска нам потребуется

fА (N) =

Асимптотическая верхняя оценка этой величины при k=N – O(N2).

Замечание. Для значений k > N/2 поиск лучше начинать с конца списка (то есть искать не самое большое, а, наоборот, самое маленькое значение элемента списка).

Для поиска значений из середины списка есть и более эффективный алгоритм. Поскольку нам нужно лишь k-тое по величине значение, то точное местоположение больших (k – 1) элементов нас на самом деле не интересует – нам достаточно знать, что они больше искомого.

Для всякого элемента из списка весь список распадается на 2 части: элементы, большие взятого, и элементы, меньшие взятого.

Если переупорядочить элементы в списке так, чтобы все большие шли за выбранным, а все меньшие – перед ним, и при этом выбранный элемент окажется на p–том месте, то мы будем знать, что он p–ый по значению.

Такое переупорядочивание требует сравнения выбранного элемента со всеми остальными – то есть (N-1) сравнение.

Если нам «повезло», и p = k, то работа закончена.

Если k < p, то нам нужно некоторое большее значение, и проделанную процедуру повторяют со 2-ой частью списка.

Если k > p, то нам нужно меньшее значение, и мы можем воспользоваться первой частью списка; при этом, однако, k следует уменьшить на число откинутых во второй части элементов.

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

FindKRec (list, start, end, k)

// list – список для просмотра

// start - индекс первого рассматриваемого элемента

// end – индекс последнего рассматриваемого элемента

// k - порядковый номер требуемого по величине элемента

if start < end then

Part (list, start, end, p)

// процедура Part разбивает список на 2 части относительно среднего по

// положению элемента

if p = k then return list [p]

else

if p > k

then return FindKRec (list, start, p-1,k)

else return FindKRec (list, p+1, end, k)

endif

endif

endif

end

Если предположить, что в среднем при разбиении список будет делиться на 2 приблизительно равные части, то ожидаемое количество сравнений:

 fА (N) = N + N/2 + N/4 + …  О(2N) (не зависит от k)

Рассмотрим работу данного алгоритма на примере списка из N=9 элементов, k=4.

1

2

3

4

5

6

7

8

9

номера элементов списка

3

7

8

5

2

1

4

6

0

список list

первое переупорядочивание относительно среднего элемента (2)

1

0

2

6

4

5

8

7

3

р=3, kр, k>p

8 сравнений

работаем со второй частью списка, ее середина – 5; первая часть остается неизменной

1

0

2

4

3

5

7

8

6

р=6, kр, k<p

5 сравнений

работаем с первой частью списка, ее середина – 4; вторая часть – без изменений

1

0

2

3

4

5

7

8

6

р=5, kр, k<p

1 сравнение

1

0

2

3

4

5

7

8

6

р=4, k=р, значение найдено

1 сравнение

ИТОГО 15 сравнений