
- •Оглавление
- •Цикл жизни программного обеспечения
- •Начальные этапы разработки по
- •Общие требования к методологии и проектированию по
- •Качество и надежность программного обеспечения
- •Показатели качества
- •Сложность комплексов программ
- •Надежность комплексов программ
- •Критерии надежности
- •Сбой, отказ, восстановление
- •Алгоритмы сортировки
- •Введение
- •Внутренняя сортировка
- •Сравнение эффективности алгоритмов сортировки
- •Простая сортировка вставками
- •Быстрая сортировка Хоара. Сортировка методом пузырька
- •Сортировка методом «турнира с выбыванием»
- •Реализация сортировки вставками. Алгоритм Шелла.
- •Сортировка слиянием. Поразрядная сортировка
- •Поиск данных
- •Введение в поиск данных
- •Последовательный поиск
- •Поиск в упорядоченной таблице
- •Бинарный поиск
- •Поиск по дереву
- •Вставка в дерево бинарного поиска
- •Удаление из дерева бинарного поиска
- •Хеширование
- •Разрешение коллизий при хешировании методом открытой адресации
- •Выбор хеш-функции
- •Объектно-ориентированное программирование
- •Введение в объектно-ориентированное программирование
- •Инкапсуляция
- •Полиморфизм
- •Конструкторы и деструкторы
- •Наследование
- •Объединения, встраиваемые функции
- •Указатели и адреса
- •Программирование параллельных вычислений
- •Введение
- •Сети. Родитель сети
- •Синхронизация процессов
- •Литература
Бинарный поиск
Наиболее эффективным методом поиска в упорядоченном массиве без использования вспомогательных индексов или таблиц является бинарный поиск. Упрощенно этот метод состоит в том, что аргумент сравнивается с ключом среднего элемента таблицы. Если они равны, то поиск успешно закончился. В противном случае поиск должен быть осуществлен аналогичным образом в верхней или нижней половине таблицы [3].
Известно, что бинарный поиск наилучшим образом может быть определен рекурсивно. Однако большие накладные расходы, связанные с рекурсией (память, время), делают ее неподходящей для использования в практических ситуациях, в которых эффективность является главным фактором. Рассмотрим не рекурсивную версию алгоритма бинарного поиска:
low=l; hi=n; search = 0;
while (low<=hi)
{
mid=(low + hi)/2;
if (key= = k[mid])
{
search = mid;
cout<<”Элемент найден.Его номер=”<< mid;
return 0;
}
else
{
if ( key<k[mid]) hi =mid-1;
else low = mid+1;
}
}
Каждое сравнение в бинарном поиске уменьшает число возможных кандидатов сравнения в 2 раза. Таким образом, максимальное число сравнений ключа, которые будут сделаны, составляет приблизительно log2n, т.е. алгоритм бинарного поиска имеет порядок O(log2n).
Отметим, что бинарный поиск может быть использован вместе с индексно-последовательной организацией таблицы и вместо последовательного поиска по индексу может быть использован бинарный поиск. Бинарный поиск может быть также использован при поиске в основной таблице, когда идентифицированы две граничные записи. Бинарный поиск практически бесполезен в ситуациях, где имеется много вставок или удалений.
Сбалансированные деревья (AVL-деревья)
Для удобства определим высоту пустого дерева как 0.
Баланс некоторого узла в дереве определяется как высота его левого поддерева минус высота его правого поддерева.
Сбалансированным бинарным деревом (деревом AVL) является такое бинарное дерево, у которого абсолютное значение баланса каждого узла 1.
Рис. 4.3. Сбалансированное бинарное дерево
Поиск по дереву
Ранее мы рассматривали построение бинарного дерева, в котором все левосторонние потомки некоторого узла с ключом key имели ключи, которые < key, а все правосторонние потомки имели ключи >= key [3].
Прохождение такого бинарного дерева в симметричном порядке дает файл, упорядоченный по возрастанию значения ключа. Такое дерево может быть использовано для бинарного поиска. Алгоритм поиска ключа key в бинарном дереве представляется следующим образом. Предполагаем, что каждый узел дерева содержит четыре поля: поле k, в котором хранится значение ключа данной записи; поле r, в котором хранится сама запись; поля left и right, которые являются указателями на поддеревья:
class item
{
public:
item *left; item *right ;
int k; char r;
};
void main()
{
item * p; // создается объект р
item * search=NULL;
…
while( p!=NULL)
{
if (p->k==key) search=p;
if( p->k>key) p=p->left;
if (p->k<key) p=p->right;
}
Отметим, что бинарный поиск фактически использует отсортированный массив как некоторое неявное дерево бинарного поиска. Средний элемент этого массива можно представить как корень такого дерева. Левую (верхнюю) половину массива (все те элементы, которые меньше чем средний элемент) можно рассматривать как левое поддерево, а правую (нижнюю) половину (все те элементы, которые больше чем средний элемент) можно рассматривать как правое поддерево.
Отсортированный массив может быть получен из дерева бинарного поиска при помощи прохождения этого дерева в симметричном порядке и вставки каждого элемента последовательно в некоторый массив по мере того, как он встречается в дереве. С другой стороны, для некоторого заданного отсортированного массива можно построить несколько соответствующих ему деревьев бинарного поиска. Рассматривая средний элемент массива как корень некоторого дерева и рассматривая рекурсивно оставшиеся элементы как левые и правые поддеревья, мы получим некоторое относительно сбалансированное дерево бинарного поиска (рис. 4.4). Рассматривая первый элемент массива в качестве корня дерева, а каждый последующий элемент как правого сына его предшественника, мы получим сильно разбалансированное дерево (рис. 4.5).
Преимущество использования дерева бинарного поиска перед отсортированным массивом заключается в том, что структура дерева позволяет выполнять эффективно операции поиска, вставки и удаления.
30 |
a1 |
47 |
a2 |
86 |
a3 |
95 |
a4 |
115 |
a5 |
130 |
a6 |
138 |
a7 |
159 |
a8 |
166 |
a9 |
184 |
a10 |
206 |
a11 |
212 |
a12 |
219 |
a13 |
224 |
a14 |
237 |
a15 |
258 |
a16 |
296 |
a17 |
307 |
a18 |
314 |
a19 |
Рис. 4.4
Рис. 4.5