
- •1. Введение
- •2. Элементы языка
- •2.1. Первые программы
- •2.2. Алфавит языка
- •2.3. Комментарии
- •2.4. Типы данных
- •2.5. Целые типы данных
- •2.6. Плавающие типы данных
- •2.7. Константы-строки, или литералы
- •2.8. Директива препроцессора define
- •2.9. Описания
- •2.10. Модификатор const
- •3. Выражения
- •3.1. Операция и выражение присваивания
- •3.2. Арифметические операции
- •3.3. Операции отношения
- •3.4. Логические операции
- •3.5. Побитовые операции
- •3.6. Сдвиги
- •?????3.8. Тернарная или условная операция
- •3.9. Операция следования
- •3.10. Приоритеты операций и порядок вычисления
- •4.6. Оператор выбора switch
- •4.7. Оператор цикла while
- •4.8. Цикл с постусловием do-while
- •4.9. Оператор for
- •4.10. Оператор безусловного перехода
- •4.11. Оператор break
- •4.12. Оператор continue
- •4.13. Оператор return
- •5. Указатели
- •5.1. Определение указателей
- •5.2. Указатели и массивы
- •5.3. Адресная арифметика
- •5.4. Символьные массивы и строки
- •5.5. Многомерные массивы
- •5.6. Указатели и многомерные массивы
- •6. Операция sizeof
- •7. Операции для работы с динамической памятью
- •7.1. Операция выделения памяти new
- •7.2. Операция освобождения памяти delete
- •8. Функции
- •8.1. Определение и вызов функции
- •8.2. Функции. Передача аргументов
- •8.3. Передача многомерных массивов
- •8.4. Указатели на функции
- •8.5. Ссылки
- •8.6. Ссылки в качестве параметров функций
- •8.7. Аргументы по умолчанию
- •8.8. Переопределение функций
- •8.9. Шаблоны функций
- •9. Объявления и определения
- •10. Область существования имени
- •11. Область видимости
- •Здесь будет напечатано
- •12. Классы памяти
- •13. Объявления объектов и типов
- •14. Имена типов
- •15. Синоним имени типа
- •16. Правила преобразования стандартных типов
- •16.1. Явные преобразования
- •16.2. Неявные преобразования стандартных базовых типов
- •16.3. Преобразование производных стандартных типов
- •17. Перечисления
- •18. Классы
- •18.1. Объявление классов
- •18.2. Конструкторы
- •18.3. Деструкторы
- •18.4. Статические члены класса
- •18.5. Указатель this
- •18.6. Статические функции-члены
- •18.7. Указатели на члены класса
- •18.8. Инициализация данных–членов класса
- •18.9. Конструктор копирования и операция присваивания
- •18.10. Дружественные функции
- •18.11. Конструктор и операция new
- •18.12. Вызов деструктора
- •19. Производные классы
- •19.1. Построение производного класса
- •19.2. Защищенные члены класса
- •19.3. Управление уровнем доступа к членам класса
- •19.4. Последовательность вызова конструктора и деструктора при построении производного класса на основе одного базового
- •19.5. Преобразования типов
- •20. Полиморфизм
- •20.1. Раннее и позднее связывание
- •20.2. Виртуальные функции
- •20.3. Абстрактные классы
- •21. Переопределение стандартных операций
- •21.1. Основные определения и свойства
- •21.2. Операции new и delete при работе с абстрактными типами
- •21.3. Использование new при создании динамического объекта абстрактного типа
- •21.4. Операция delete
- •21.5. Преобразование типов
- •22. Некоторые особенности переопределенных операций
- •22.2. Операция [ ]
- •23. Классы и шаблоны
- •24. Списки
- •24.1. Операции над односвязными списками
- •24.2. Двунаправленные и кольцевые списки
- •24.3. Операции над кольцевыми списками
- •25. Стеки
- •25.1. Реализация стека через массив
- •25.2. Реализация стека через динамическую цепочку звеньев
- •26. Двоичные деревья
- •26.1. Определение и построение
- •26.2.Таблицы
- •27. Список литературы
26.2.Таблицы
Построенное в последнем примере дерево часто называют деревом поиска. Дерево поиска иногда используют для построения таблиц, в которых хранятся различные сведения, обычно в виде структур. При этом для упорядочения структур они обычно именуются и чтобы организовать эффективный поиск структуры по ее имени, нужно иметь возможность сравнивать любые два имени и устанавливать, какое из них “больше”, а какое –“меньше”. Имя структуры в таблице часто называют ключом структуры. В качестве ключа часто используют целые числа или строки одинаковой длины.
Над таблицей как структурой данных определены операции:
- поиск в таблице структуры с заданным ключом;
- включение в таблицу структуры с заданным ключом;
- исключение из таблицы структуры с заданным ключом.
Мы рассмотрим организацию таблиц в виде двоичного дерева. В примере кодирования ключом служили сами числа из файла NUM.DAT. Фактически мы уже рассмотрели операции поиска в дереве и включения элемента в дерево по заданному ключу. Сейчас оформим в виде функции операцию исключение элемента с заданным ключом из дерева.
Н
епосредственное
удаление структуры реализуется просто,
если удаляемая вершина дерева является
конечной или из нее выходит только одна
ветвь (рис. 9):
Рис. 9. Удаление элемента из дерева, когда удаляемая вершина
является конечной или из нее выходит только одна ветвь
Трудность состоит в удалении вершины, из которой выходят две ветви. В этом случае нужно найти подходящее звено дерева, которое можно было бы вставить на место удаляемого, причём это подходящее звено должно просто перемещаться. Такое звено всегда существует: это либо самый правый элемент левого поддерева, либо самый левый элемент правого поддерева. В первом случае для достижения этого звена необходимо перейти в следующую вершину по левой ветви, а затем переходить в очередные вершины только по правой ветви до тех пор, пока очередной указатель не будет равен NULL. Во втором случае необходимо перейти в следующую вершину по правой ветви, а затем переходить в очередные вершины только по левой ветви до тех пор, пока очередной указатель не станет равен NULL. Такие подходящие звенья могут иметь не более одной ветви. Ниже (рис. 9) схематично изображено исключение из дерева вершины с ключом 50:
100 100
20 120 20 120
15 50 130 15 35 130
=>
30 55 30 55
28 35 60 28 33 60
33
Рис. 9. Исключение из дерева вершины с ключом 50
Приведём программу, реализующую дерево поиска со всеми операциями с помощью класса tree и несколько изменённой функции insert.
#include <fstream.h>
struct node {
int key, code;
node *left, *right;
node (int k, int c, node *l, node *r){ key = k; code = c; left = l; right = r; }
};
int f(int);
class tree {
node *root;
void print (node *r); // Печать с указателя r.
public:
int insert (int);
void del (int);
tree () { root = 0; } // Создание пустого дерева.
node *&find (node *&r, int key); // Поиск элемента в поддереве
// с вершиной r.
node *&find (int key) { // Поиск по всему дереву.
return ( find(root, key) );
}
void print () { print (root); } // Печать всего дерева.
};
int tree::insert (int key){ // Возвращает код.
node* t = root;
node* old;
int k;
if (root = = 0 ) { k = f( key );
root = new node( key, k, 0, 0 );
return k;
}
while (t !=0){
old = t;
if (t-> key = = key ){
return t->code; }
if ( t->key > key ) t = t->left;
else t = t->right; }
k = f (key);
if ( old->key > key) old->left = new node ( key, k, 0, 0);
else old->right = new node ( key, k, 0, 0);
return k; }
node *&tree::find ( node *&r, int key){ // Рекурсивная функция
if ( r = = 0) return r; // поиска в поддереве r.
if ( r->key = = key ) return r;
if ( r->key > key ) return ( find ( r->left, key ));
else
return ( find ( r->right, key ));
}
void del_el ( node *&r, node *q){ // Рекурсивная функция
if ( r->right = = 0 ) { // удаления элемента,
q->key = r->key; // на который указывает q.
q->code = r->code;
q = r; r = r-> left;
} else
del_el ( r->righ, q );
}
void tree::del (int key){ // Удаление элемента с ключом key.
{ node *&q = find ( key); // Установка указателя q на удаляемый
// элемент.
if ( q = = 0) { cout << “Элемента с ключом “ << key <<“ нет\n“;
return; }
if ( q->right = = 0) q = q->left;
else
if ( q->left = = 0) q = q->right;
else
del_el ( q->left, q);
}
void print ( node *r){ // Глобальная функция печати
// элемента дерева r.
cout << r->key << “ “ << r->code << “ “;
}
void tree::print ( node *r) { // Рекурсивная функция печати
if (r != 0) { // поддерева, начиная с r.
::print ( r);
print ( r->left );
print ( r->right );
}
}
ifstream numb (“num.dat“), cod (“cod.dat“);
int f ( int k ){ int i, j;
cod.seekg ( 0 );
for ( i = 1; i <= k; i++) cod >> j;
return j;
}
void main (){
cout << “-------------------------------------------------------------------\n“;
int key;
tree t;
while ( num.peek () != EOF) {
numb >> key;
cout << t.insert ( key ) << “ “; // Вставка и печать элемента,
} // или просто печать, если
cout << ‘\n’; // элемент уже есть.
t.print ();
cout << “\n\n“;
key = 50;
t.del ( key );
t.print ();
cout << ‘\n’;
}
Заметим, что рекурсивная функция del_el вызывается, если из удаляемой вершины направлены два ребра дерева. Она спускается до самого правого звена левого поддерева удаляемого элемента *q, а затем заменяет значения членов key и code в *q соответствующими значениями полей key и code вершины *r. После этого вершину, на которую указывает r, можно исключать с помощью оператора r = r->left. Можно видоизменить эту функцию, освобождая память, занимаемую удалённым звеном с помощью операции delete.