Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

g30zAZLYUG

.pdf
Скачиваний:
3
Добавлен:
15.04.2023
Размер:
1.89 Mб
Скачать

Delete(y, pv->lptr);// Продолжить поиск в ЛПД

}

else

if(y>pv->key)

{

Delete(y, pv->rptr); // Продолжить поиск в ППД

}

else

{// Если элемент найден

if (pv->count>1) // Если счётчик больше 1

{

pv->count--; // Уменьшаем на 1

}

else

{// Если счётчик равен 1, удаляем элемент buf=pv;

//Если у удаляемой вершины нет левого поддерева, то

if(buf->lptr ==NULL)

{

// на её место помещаем её правого потомка pv=buf->rptr;

}

else

// Если у удаляемой вершины нет правого поддерева, то if (pv->rptr ==NULL)

{

// на её место помещаем её левого потомка pv=buf->lptr;

}

else

// Если у удаляемой вершины есть и левое, и правое поддеревья,

{

// то эту ситуацию обрабатывает процедура Del

Del(buf, buf->lptr);

};

// Освобождаем место, занимаемое удаляемой вершиной free(buf);

}

}

}

}

2.10. Осталось разобраться с процедурой Del, которая производит удаление вершины V, имеющей двух потомков. На вход процедуры Del поступает

81

адрес удаляемой вершины и

адрес её левого потомка (другими словами, адрес корня левого поддерева удаляемой вершины V).

Вэтом случае надо сначала найти самый правый элемент W левого поддерева удаляемой вершины V, а затем отправить его на место удаляемой вершины. Единственного потомка вершины W надо прицепить к предку этой вершины.

Начиная от корня левого поддерева удаляемой вершины V, двигаемся всё время вправо, пока не упрёмся в вершину с нулевым правым указателем. Это и есть искомая вершина W.

if (pv1->rptr!=NULL)

{

Del(pv,pv1->rptr);

}

После выполнения этого цикла информация о вершине W хранится по адресу pv1. Дальше действуем так: переносим ключ и счётчик вершины W на место удаляемой вершины V:

pv->key=pv1->key; pv->count=pv1->count;

Переопределяем адрес: вершина W занимает место удаляемой вершины V:

pv=pv1;

По адресу pv1 заносим адрес левого потомка вершины W: pv1=pv1->lptr;

В результате выполнения последнего оператора предок вершины W теперь ссылается не на неё, а на её левого потомка. Таким образом, нам не пришлось «беспокоить» преобразованиями предка вершины W: он как ссылался на адрес, хранимый в переменной pv1, так и ссылается на него – мы поменяли само значение переменной pv1.

void Del(NODE *&pv, NODE *&pv1)

{

if (pv1->rptr!=NULL)

{

Del(pv,pv1->rptr);

}

else

{

pv->key=pv1->key; pv->count=pv1->count; pv=pv1; pv1=pv1->lptr;

};

}

82

Запустите программу на выполнение. Используя пользовательское меню, добавьте в дерево несколько новых элементов, а затем их поочерёдно удалите.

ЗАДАНИЕ № 2. Хранение набора записей в виде дерева поиска

Модифицируйте созданную программу так, чтобы в дереве поиска хранились не целые числа, а упорядоченный по числовому полю набор данных из Вашего варианта (см. задание № 1 лабораторной работы № 1). Если в наборе данных Вашего варианта несколько числовых полей, то выберите любое из них.

Указание. Поле счётчика count Вам не понадобится, в качестве ключевого поля key надо будет использовать любое числовое поле из Вашего набора данных. Таким образом, Ваша структура SearchTree будет содержать шесть полей: четыре поля из Вашего набора данных плюс два поля указателей.

Как и в предыдущих лабораторных работах, предусмотрите ввод элементов не с консоли, а из входного текстового файла.

Фрагмент добавления элементов посредством пользовательского меню можете закомментировать.

Работающую версию программы, а также программный код, предъявите преподавателю.

Контрольные вопросы

1.В чём заключается дихотомический (логарифмический) поиск?

2.Какова оценка временной сложности дихотомического поиска?

3.Что представляет собой структура данных двоичное (бинарное) дерево?

4.Перечислите операции, определённые над двоичным деревом.

5.Какие рекурсивные способы обхода бинарных деревьев Вам известны?

6.Как определяется высота двоичного дерева?

7.Какое двоичное дерево называется полным?

8.Запишите формулу, связывающую высоту полного дерева и количество его вершин.

9.Сформулируйте определение дерева поиска.

10.Каким образом производится добавление вершин в дерево поиска?

11.Какие ситуации, требующие различной обработки, возможны при удалении вершины из дерева поиска?

12.Если удаляемая вершина имеет двух потомков, то на какую вершину её надо заменить?

13.Оцените теоретическую сложность операций добавления и удаления элементов при работе с деревом поиска.

14.Как с помощью дерева поиска организовать подсчёт количества вхождений каждого из элементов входной последовательности?

83

Лабораторная работа № 6. Исчерпывающий поиск

Существуют задачи, для решения которых не найден алгоритм полиномиальной сложности, но эти задачи можно решить путём перебора большого (но конечного!) числа вариантов. Количество этих вариантов зависит от параметров задачи и часто бывает экспоненциальным. Такие за-

дачи называются трудно решаемыми.

Примеры трудно решаемых задач.

1. Задача коммивояжёра

Постановка задачи. Имеется n данных городов. Известна стоимость прямого проезда из города i в город j (i j; i, j = 1, 2, ..., n). Требуется найти циклический маршрут, который проходит через каждый из n данных городов в точности один раз и при этом имеет наименьшую стоимость.

2. Задача об оптимальной выборке (о рюкзаке)

Постановка задачи. Имеется n объектов, пронумерованных от 1 до n. Каждый из этих объектов i характеризуется весом wi и ценностью vi. Оптимальной называется выборка, имеющая максимальную сумму ценностей при заданном ограничении на сумму весов. Требуется извлечь оптимальную выборку из данного множества объектов.

3. Задача о выполнимости

Постановка задачи. Булева функция задана в виде конъюнктивной нормальной формы:

 

s

 

 

x

2

... x

r

 

f(x1,..., xn) =

& ( x

1

).

 

i1

 

i2

 

ir

 

 

 

 

 

 

 

 

 

i 1

 

 

 

 

 

 

 

Требуется проверить, существует ли набор (a1, a2,..., an) значений переменных (x1, x2,..., xn), обращающих функцию f в 1?

4. Задача о раскраске графа

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

5. Задача о клике

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

Пример. Граф, изображенный на рисунке, имеет клику мощности 4.

Требуется проверить, существует ли в графе F клика из m вершин?

84

На первом курсе, в рамках одной из дисциплин математического блока, Вы познакомились с такими комбинаторными объектами, как размещения, перестановки, сочетания и т.д.

В данной лабораторной работе мы займёмся разработкой рекурсивных процедур, генерирующих эти и другие комбинаторные объекты. Для простоты все комбинации будем извлекать из множества

A = {1, 2, ..., n}.

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

Размещения

Размещениями называются комбинации, составленные из n элементов по m элементов, которые отличаются либо составом элементов, либо порядком; m n.

Пример. Пусть n = 4, m = 2, т.е. из множества A = {1, 2, 3, 4} извлекаем два элемента. Все возможные размещения из 4-х по 2:

(1, 2), (2,1), (1, 3), (3, 1), (1, 4), (4, 1), (2, 3), (3, 2), (2, 4), (4,2), (3, 4), (4, 3).

Общее число размещений из

n по m обозначается

Am

и вычисля-

 

 

 

 

n

 

ется по формуле:

 

 

 

 

 

Am

n!

.

 

 

 

 

 

 

 

 

 

 

n

(n m)!

 

 

 

 

 

 

 

Размещения с повторениями

Размещениями с повторениями называются размещения, элементы в которых могут повторяться.

Пример. Пусть n = 2, m = 3, т.е. из множества A = {1, 2} извлекаем три элемента. Все возможные размещения с повторениями из 2-х по 3:

(1,1,1), (1,1,2), (1,2,1), (2,1,1), (1,2,2), (2,1,2), (2,2,1), (2,2,2).

Общее число размещений с повторениями из n по m обозначается Anm и вычисляется по формуле:

Anm = nm.

Перестановки

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

85

Пример. Пусть n = 3, т.е. из множества A = {1, 2,3} извлекаем все три элемента в различном порядке. Все возможные перестановки из 3-х элементов:

(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1).

Общее число перестановок обозначается Pn и вычисляется по фор-

муле:

Pn = n!

Перестановками с повторениями

Перестановками с повторениями называются перестановки, элементы в которых могут повторяться.

Пример. Пусть n = 2, m = 3, т.е. из множества A = {1, 2} извлекаем три элемента. Все возможные перестановки с повторениями из 2-х по 3:

(1, 1, 2), (1, 2, 1), (2, 1, 1), (1, 2, 2), (2, 1, 2), (2, 2, 1).

Сравните эти комбинации с размещениями с повторениями. Как видите, здесь отсутствуют комбинации (1,1,1) и (2,2,2). В первой из них нет ни одного элемента 2, во второй – ни одного элемента 1. Другими словами, в перестановке с повторениями, в отличие от размещения с повторениями, должны присутствовать все элементы (хотя бы по одному разу), которые содержатся во множестве A.

Число различных перестановок, имеющих состав ( 1, 2,..., n), обозначается P( 1, 2,..., n) и вычисляется по формуле:

P( 1, 2,..., n) =

m!

,

где m = 1 + 2 + ... + n.

 

 

 

1! 2

! n!

 

 

 

Сочетания

Сочетаниями называются комбинации, составленные из n элементов по m элементов и отличающиеся хотя бы одним элементом; m n. Сочетания различаются только составом элементов; порядок элементов здесь не имеет значения.

Пример. Пусть n = 4, m = 2, т.е. из множества A = {1, 2, 3, 4} извлекаем два элемента. Все возможные сочетания из 4-х по 2:

(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4).

Число всех сочетаний из n по m обозначается Cnm и вычисляется по формуле:

C m

n!

.

 

 

 

 

n

m! (n m)!

 

 

 

86

Сочетания с повторениями

Сочетаниями с повторениями называются сочетания, элементы в которых могут повторяться.

Пример. Пусть n = 2, m = 3, т.е. из множества A = {1, 2} извлекаем три элемента. Все возможные сочетания с повторениями из 2-х по 3:

(1,1,1), (1,1,2), (1,2,2), (2,2,2).

Общее число сочетаний с повторениями из n по m обозначается

 

 

 

 

 

 

 

 

Cnm и вычисляется по формуле:

 

 

 

 

 

 

 

 

(n m 1)!

Cnm m1.

 

 

Cnm = P(n 1, m) =

 

 

 

(n 1)! m!

 

 

 

 

 

 

 

Композиции

Последовательность (z1, z2, …, zn) называется композицией натурального числа m, если

z1 + z2 + …+ zn = m,

причём учитывается порядок чисел zi; i = 1, 2, …, n.

 

Как правило, представляют интерес композиции, в которых

1) zi

− число целое неотрицательное, т.е. zi N {0}; i = 1, 2, …, n.

2) zi

− число натуральное, т.е.

zi N; i = 1, 2, …, n.

Пример. Пусть n = 2, m = 3, т.е. надо найти все способы, которыми можно представить число 3 в виде суммы двух чисел.

В первом случае искомых комбинаций четыре:

(0, 3), (1, 2), (2, 1), (3, 0).

Во втором случае таких комбинаций две: (1, 2), (2, 1).

Разбиение

Последовательность (z1, z 2, …, z n) называется разбиением натурального числа m, если

z1 + z2 + …+ zn = m,

zi N (i = 1, 2, …, n)

и порядок чисел zi не важен.

 

Пример. Все разбиения числа m = 4:

при n = 1: (4),

при n = 2: (1, 3), (2, 2), при n = 3: (1, 1, 2), при n = 4: (1, 1, 1, 1).

87

Рассмотрим алгоритмы решения задач 1–5 методом полного пере-

бора.

1. Задача коммивояжёра

Решение. Фиксируем город № n в качестве начала и конца циклического маршрута, после чего

генерируем все возможные перестановки элементов множества {1, 2, …, n−1},

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

Вкачестве ответа задачи выбираем тот циклический маршрут (возможно, не единственный), который имеет наименьшую стоимость.

2. Задача об оптимальной выборке (о рюкзаке)

Решение. Каждой выборке, т.е. подмножеству S множества A = {1, 2,

..., n}, поставим во взаимно однозначное соответствие булев вектор Vs = (v1, v2,…, vn) по правилу:

1,

i S;

vi =

i S.

0,

Вектор Vs можно рассматривать как размещение с повторениями из 2-х по n. Далее действуем так:

генерируем все возможные размещения с повторениями из элементов множества {0, 1} по n,

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

Вкачестве ответа задачи выбираем (возможно, не единственную) оптимальную выборку, т.е. ту, которая имеет максимальную сумму ценностей при заданном ограничении на сумму весов.

3. Задача о выполнимости

Решение. Генерируем все возможные наборы переменных (x1, x2,..., xn), т.е. все возможные размещения с повторениями из элементов множества {0, 1} по n. Для каждой комбинации подсчитываем значение данной булевой функции f(x1, x2,..., xn).

4. Задача о раскраске графа

Решение. Пометим краски элементами множества K = {1, 2, ..., k}. Каждой раскраске графа поставим во взаимно однозначное соответствие вектор X = (x1, x2,..., xn) по правилу:

(xi = j) (i –я вершина раскрашена в j-й цвет); i = 1, 2,…, n; j = 1, 2,…, k.

88

Вектор X можно рассматривать как размещение с повторениями из k по n. Далее действуем так:

генерируем все возможные размещения с повторениями из элементов множества K по n,

для каждой комбинации проверяем, является ли раскраска, соответствующая этой комбинации, правильной?

5. Задача о клике

Решение. Пусть n − количество вершин данного графа. Генерируем все возможные сочетания из n по m. Для вершин, входящих в каждое сочетание, проверяем, являются ли любые две из них смежными?

п. 1. Генерация комбинаторных объектов

Для генерации комбинаторных объектов будем использовать алгоритмы с возвратом, позволяющие обойти всё дерево решений.

Разрабатывать процедуры будем не в том порядке, как они представлены во вводной теоретической части, а в порядке «от простого к сложному». Проще всего сгенерировать размещения с повторениями. С них и начнём.

1.1.Отработав пункты 1.1 – 1.7 лабораторной работы № 2, создайте новый проект с именем, например, lab_06.

1.2.После строки

using namespace std;

в области объявления глобальных типов и переменных вставьте объявление двух констант и статического массива:

//MAX_N - Максимально возможное количество эл-в во множестве А

#define MAX_N 50

//MAX_M - Максимально возможное количество элементов в комбинации

#define MAX_M 100 int myArray[MAX_M];

1.3.В следующей строке объявите целочисленные переменные:

int n, m, count=0;

Здесь

n – количество элементов во множестве A = {1, 2, ..., n}, из которого извлекаются элементы;

m – количество элементов в комбинации;

счётчик count – номер очередной сгенерированной комбинации.

1.4.В процедуре main организуйте ввод с консоли значений переменных n и m. Например, так:

89

cout << "Введите n - мощность множества A\n"; cin >> n;

cin.ignore();

cout << "Введите m - количество элементов в комбинации\n"; cin >> m;

cin.ignore();

1.5. После объявления глобальных переменных перед процедурой main вставьте код рекурсивной процедуры RP, подбирающей значение k-й компоненты очередного размещения с повторениями:

void RP(int k)

{

int i,j;

for (i=1; i<=n; i++) // Цикл по всем элементам множества А

{

myArray[k]=i; // Выбираем i в качестве k-го элемента

if (k < m) // Если комбинация сформирована не полностью,

{

RP(k+1);

// то ищем k+1-ю компоненту

}

 

else

// Если комбинация полностью сформирована,

{

 

count++;

cout << "Комбинация № " << count << "\t"; for (j=1; j<=m; j++)

{

cout << myArray[j] << "\t";

// то печатаем её

};

 

cout << "\n";

 

}

}

}

Здесь в цикле по всем элементам множества A выбираем i-й элемент этого множества в качестве k-й компоненты формируемой комбинации.

Если в комбинации меньше чем m элементов, то она сформирована не полностью, и мы осуществляем рекурсивный вызов процедуры RP для поиска следующей, k+1-й, компоненты. В противном случае печатаем саму комбинацию и её номер.

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

1.5. Осталось добавить вызов процедуры RP в процедурe main. Поскольку генерировать комбинацию мы начинаем с 1-го элемента, то на вход процедуре RP передаём единицу:

90

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]