МИНОБРНАУКИ РОССИИ
Санкт-Петербургский государственный
электротехнический университет
«ЛЭТИ» им. В.И. Ульянова (Ленина)
Кафедра вычислительной техники
Отчет по лабораторной работе №3
по дисциплине «Алгоритмы и структуры данных»
Студент гр. 930 |
|
Преподаватель |
Колинько П.Г. |
Тема: Деревья
Содержание
Введение ........................................................................................................ 3
Задание ........................................................................................................... 3
Постановка задачи и описание решения ..................................................... 3 Контрольные тесты ...................................................................................... 4
Вывод ............................................................................................................. 6
Список использованных источников........................................................... 7
Текст программы ........................................................................................... 8
Цель работы
Исследование алгоритмов для работы с двоичным деревом
Задание
В двоичном дереве сделать обратную разметку, обойти дерево в глубину и подсчитать количество левых листьев
Постановка задачи и описание решения
Для представления дерева в памяти предложен естественный способ – разветвляющийся список. Узлы дерева – объекты, связи между которыми осуществляются через указатели. Для создания дерева достаточно объявить корень, членами которого являются левый и правый указатели на узлы дерева. Для работы с деревом удобнее иметь отдельный класс “дерево”, в котором собираются данные о всем дереве и функции-члены для работы с ним, в то время как для узлов сделать отдельный класс и сделать его дружественным для класса “дерево”
В программе под левым листом понимается узел, который находится в указателе left предыдущего узла и у которого отсутствуют сыновья. То есть если у узла нет сыновей, он единственный сын предыдущего узла, но при этом он находится в указателе right, левым листом его считать нельзя!
Итак, в программе первым делом создается дерево. Функция создания дерева вызывает рекурсивную функцию создания узла.
Для создания дерева в памяти применяется прямой алгоритм обхода (для графов общего вида – обход в глубину. Для дерева эти термины эквивалентны). Первым шагом алгоритма является проверка необходимости создания узла. Если ответ положительный, узел создается, и в нем заполняются информационные поля. Далее заполняются поля указателей на каждого сына: для получения значения указателя алгоритм запускается рекурсивно. Результат – указатель на вновь созданный узел или нуль, если узел не создан
После создания дерева вызывается функция его обхода в глубину. Я решил модернизировать эту функцию, добавив в нее возможность нахождения левых листьев дерева. Для этого создается стек узлов дерева, в которое вносятся и обрабатываются необходимые узлы. Если у очередного узла у его левого сына отсутствуют сыновья, то счетчик левых листьев увеличивается на единицу.
В работе я решил не использовать перемещающий конструктор и перемещающее присваивание, поскольку в методичке написано: “сделать невозможным использование тех конструкторов, которые на самом деле не нужны”
Контрольные тесты
Для тестов необходимо прогнать программу генерацией случайного дерева и ввести данные дерева вручную
В программе строку с ручным вводом я задокументировал, поскольку для отладки лучше использовать случайную генерация (можно раздокументировать строку и самому убедиться в ее правильности)
Ввод дерева с клавиатуры
Генерация случайного дерева
Оценки временной сложности
Создание дерева: O(n), где n – количество листьев
Обход в глубину: O(n)
Вывод дерева: O(n)
Вывод
В данной работе были исследованы алгоритмы для работы с двоичным деревом.
Был совершен обход дерева в глубину: кроме обхода каждой вершины были выявлены левые листья: для этого создается стек узлов дерева, в которое вносятся и обрабатываются необходимые узлы
Для отладки рекомендуется использовать случайную генерацию дерева.
Дерево можно использовать в тех случаях, когда необходимо выстроить иерархию объектов.
Список использованных источников
Стивен Прата: Язык программирования С++. Лекции и упражнения. - 2012. – 1248 с.
Колинько П.Г.: Методические указания по дисциплине “Алгоритмы и структуры данных, часть 1”. – 2020. – 64 с.
Видеокурс по языку программирования С++ // URL: https://www.youtube.com/playlist?list=PLQOaTSbfxUtCrKs0nicOg2npJQYSPGO9r
Текст программы
//файл main.cpp
#include "tree.h"
#include "tree.cpp"
int main()
{
int n = 0, leftcount = 0;
char leaves[10]{};
Tree Tr('a', 'z', 6);
srand(time(nullptr));
setlocale(LC_ALL, "Russian");
Tr.MakeTree();
if (Tr.exist())
{
Tr.OutTree();
cout << "\nОбход в глубину: ";
n = Tr.DFS(leftcount, *leaves);
cout << "\nПройдено узлов: " << n;
}
else cout << "Дерево пусто!\n";
cout << "\nКоличество левых листьев: " << leftcount << " (";
for (int i=0; leaves[i]; i++) cout << leaves[i]; cout << ")\n";
cout << "===Конец===\n";
return 0;
}
//файл tree.h
#include <iostream>
#include <cstring>
#include <ctime>
using namespace std;
#pragma once
class Node{
private:
char d; //node's tag
Node *left;
Node *right;
public:
Node(): left(nullptr), right(nullptr) {}
~Node() {if (left) delete left; if (right) delete right; } //destructor
friend class Tree; //it is neeeded for class "Tree" to access to data of class "Node"
};
class Tree{
private:
Node *root;
char num, maxnum; // counter of tags and max tag
int maxrow, offset; //max depth and root offset
char **SCREEN; //allocation of memory for output
Node* MakeNode(int depth); // subtree creation
void OutNodes(Node* v, int r, int c); //subtree output // (r, c) - some point on the screen
public:
Tree(char num, char maxnum, int maxrow);
~Tree();
void MakeTree() {root = MakeNode(0);} //tree generation
bool exist() {return root != nullptr;}
int DFS(int &c, char& leaves); //depth first search
void OutTree(); //tree output
};
template <class Item> class Stack
{
Item *S;//stack itself
int t; //stack size
public:
Stack(int maxt): S(new Item[maxt]), t(0) {}
int empty() const {return t == 0;}
void push(Item item) { S[t++] = item;}//add
Item pop() {return (t? S[--t] : 0);} // return and delete
~Stack() {delete S;}
};
//файл tree.cpp
#include "tree.h"
Tree :: Tree(char nm, char mnm, int mxr):
num(nm), maxnum(mnm), maxrow(mxr), offset(40), root(nullptr), SCREEN(new char* [maxrow])
{
for (int i=0; i<maxrow; ++i)
SCREEN[i] = new char[80];
}
Tree :: ~Tree()
{
for (int i=0; i<maxrow; ++i)
delete [] SCREEN[i];
delete [] SCREEN;
delete root;
}
Node *Tree:: MakeNode(int depth) //tree generation
{
Node *v = nullptr;
int Y = (depth < rand()%6 + 1) && (num <= 'z');
//cout << "Node (" << num << ',' <<depth << ")1/0: "; cin >> Y;
if (Y)
{ //node is creating, if Y
//it is made for decreasing the tree
v = new Node;
v->left = MakeNode(depth + 1);
v->right = MakeNode(depth + 1);
v->d = num++; //in reversed
//the logic: at first you make Nodes (links)
//and then mark them
}
return v;
}
void Tree:: OutTree()
{
int i;
for (i=0; i < maxrow; ++i)
memset(SCREEN[i], '.', 80);
OutNodes(root, 1, offset);
for (i=0; i<maxrow; ++i)
{
SCREEN[i][79] = 0;
cout << endl << SCREEN[i];
}
cout << endl;
}
void Tree::OutNodes(Node *v, int r, int c)
{
if (r && c && (c<80))
SCREEN[r-1][c-1] = v->d;
if (r < maxrow)
{
if (v->left) OutNodes(v->left, r + 1, c - (offset >> r)); //left son
if (v->right) OutNodes(v->right, r + 1, c + (offset >> r)); //right son
}
}
int Tree::DFS(int &c, char& leaves)
{
char *p = &leaves;
const int MaxS = 20;
int count = 0, i=0;
Stack <Node* > S(MaxS); //Node stack creation
S.push(root);
Node *buff = new Node;
while (!S.empty())
{
Node* v = S.pop();
cout << v->d;
count++;
if (v->right) S.push(v->right);
if (v->left) S.push(v->left);
if (!S.empty() && v) cout << '_';
buff = v->left;
if (buff && !buff->left && !buff->right)
{
c++;
p[i++] = v->left->d;
}
}
return count;
}
Санкт-Петербург
2020