Добавил:
Кафедра ВТ Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
10
Добавлен:
08.06.2022
Размер:
72.85 Кб
Скачать

МИНОБРНАУКИ РОССИИ

Санкт-Петербургский государственный

электротехнический университет

«ЛЭТИ» им. В.И. Ульянова (Ленина)

Кафедра вычислительной техники

Отчет по лабораторной работе №3

по дисциплине «Алгоритмы и структуры данных»

Студент гр. 930

Преподаватель

Колинько П.Г.

Тема: Деревья

Содержание

Введение ........................................................................................................ 3

Задание ........................................................................................................... 3

Постановка задачи и описание решения ..................................................... 3 Контрольные тесты ...................................................................................... 4

Вывод ............................................................................................................. 6

Список использованных источников........................................................... 7

Текст программы ........................................................................................... 8

Цель работы

    1. Исследование алгоритмов для работы с двоичным деревом

    2. Задание

В двоичном дереве сделать обратную разметку, обойти дерево в глубину и подсчитать количество левых листьев

    1. Постановка задачи и описание решения

Для представления дерева в памяти предложен естественный способ – разветвляющийся список. Узлы дерева – объекты, связи между которыми осуществляются через указатели. Для создания дерева достаточно объявить корень, членами которого являются левый и правый указатели на узлы дерева. Для работы с деревом удобнее иметь отдельный класс “дерево”, в котором собираются данные о всем дереве и функции-члены для работы с ним, в то время как для узлов сделать отдельный класс и сделать его дружественным для класса “дерево”

В программе под левым листом понимается узел, который находится в указателе left предыдущего узла и у которого отсутствуют сыновья. То есть если у узла нет сыновей, он единственный сын предыдущего узла, но при этом он находится в указателе right, левым листом его считать нельзя!

Итак, в программе первым делом создается дерево. Функция создания дерева вызывает рекурсивную функцию создания узла.

Для создания дерева в памяти применяется прямой алгоритм обхода (для графов общего вида – обход в глубину. Для дерева эти термины эквивалентны). Первым шагом алгоритма является проверка необходимости создания узла. Если ответ положительный, узел создается, и в нем заполняются информационные поля. Далее заполняются поля указателей на каждого сына: для получения значения указателя алгоритм запускается рекурсивно. Результат – указатель на вновь созданный узел или нуль, если узел не создан

После создания дерева вызывается функция его обхода в глубину. Я решил модернизировать эту функцию, добавив в нее возможность нахождения левых листьев дерева. Для этого создается стек узлов дерева, в которое вносятся и обрабатываются необходимые узлы. Если у очередного узла у его левого сына отсутствуют сыновья, то счетчик левых листьев увеличивается на единицу.

В работе я решил не использовать перемещающий конструктор и перемещающее присваивание, поскольку в методичке написано: “сделать невозможным использование тех конструкторов, которые на самом деле не нужны”

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

Для тестов необходимо прогнать программу генерацией случайного дерева и ввести данные дерева вручную

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

  1. Ввод дерева с клавиатуры

  1. Генерация случайного дерева

Оценки временной сложности

Создание дерева: O(n), где n – количество листьев

Обход в глубину: O(n)

Вывод дерева: O(n)

Вывод

В данной работе были исследованы алгоритмы для работы с двоичным деревом.

Был совершен обход дерева в глубину: кроме обхода каждой вершины были выявлены левые листья: для этого создается стек узлов дерева, в которое вносятся и обрабатываются необходимые узлы

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

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

Список использованных источников

  1. Стивен Прата: Язык программирования С++. Лекции и упражнения. - 2012. – 1248 с.

  2. Колинько П.Г.: Методические указания по дисциплине “Алгоритмы и структуры данных, часть 1”. – 2020. – 64 с.

  3. Видеокурс по языку программирования С++ // 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

Соседние файлы в папке 3 семестр - Колинько