├── image.png ├── BinarySearchTree ├── main.cpp ├── BinarySearchTree.pro ├── bst_about_window.h ├── renderarea.h ├── bst_properties_window.h ├── mainwindow.h ├── bst_about_window.cpp ├── renderarea.cpp ├── bst_properties_window.cpp ├── mainwindow.cpp └── binarysearchtree.h ├── .gitignore └── README.md /image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nahrens007/BinarySearchTreeGui/HEAD/image.png -------------------------------------------------------------------------------- /BinarySearchTree/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | * 7 | * Begins the program. 8 | */ 9 | 10 | #include "mainwindow.h" 11 | #include 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | QApplication a(argc, argv); 16 | MainWindow w; 17 | w.show(); 18 | 19 | return a.exec(); 20 | } 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # C++ objects and libs 2 | 3 | *.slo 4 | *.lo 5 | *.o 6 | *.a 7 | *.la 8 | *.lai 9 | *.so 10 | *.dll 11 | *.dylib 12 | 13 | # Qt-es 14 | 15 | /.qmake.cache 16 | /.qmake.stash 17 | *.pro.user 18 | *.pro.user.* 19 | *.qbs.user 20 | *.qbs.user.* 21 | *.moc 22 | moc_*.cpp 23 | qrc_*.cpp 24 | ui_*.h 25 | Makefile* 26 | *build-* 27 | 28 | # QtCreator 29 | 30 | *.autosave 31 | 32 | # QtCtreator Qml 33 | *.qmlproject.user 34 | *.qmlproject.user.* 35 | 36 | # QtCtreator CMake 37 | CMakeLists.txt.user 38 | 39 | #QTCreator Build Files 40 | build*/ 41 | 42 | -------------------------------------------------------------------------------- /BinarySearchTree/BinarySearchTree.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2016-12-10T13:48:57 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = BinarySearchTree 12 | TEMPLATE = app 13 | 14 | 15 | SOURCES += main.cpp\ 16 | mainwindow.cpp \ 17 | renderarea.cpp \ 18 | bst_properties_window.cpp \ 19 | bst_about_window.cpp 20 | 21 | HEADERS += mainwindow.h \ 22 | binarysearchtree.h \ 23 | renderarea.h \ 24 | bst_properties_window.h \ 25 | bst_about_window.h 26 | 27 | FORMS += 28 | -------------------------------------------------------------------------------- /BinarySearchTree/bst_about_window.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | */ 7 | 8 | #ifndef BST_ABOUT_WINDOW_H 9 | #define BST_ABOUT_WINDOW_H 10 | 11 | #include "binarysearchtree.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | class BST_About_Window 20 | { 21 | public: 22 | BST_About_Window(); 23 | ~BST_About_Window(); 24 | void show(); 25 | void close(); 26 | 27 | private: 28 | QMainWindow *window; 29 | QVBoxLayout *labelLayout; 30 | QHBoxLayout *containerLayout; 31 | QWidget *container; 32 | QVBoxLayout *mainLayout; 33 | QWidget *centralWidget; 34 | }; 35 | 36 | #endif // BST_ABOUT_WINDOW_H 37 | -------------------------------------------------------------------------------- /BinarySearchTree/renderarea.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | */ 7 | 8 | #ifndef RENDERAREA_H 9 | #define RENDERAREA_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include "binarysearchtree.h" 16 | #include 17 | 18 | class RenderArea : public QWidget 19 | { 20 | Q_OBJECT 21 | public: 22 | explicit RenderArea(BinarySearchTree *bst, QWidget *parent = 0); 23 | 24 | QSize minimumSizeHint() const override; 25 | QSize sizeHint() const override; 26 | void zoomIn(); 27 | void zoomOut(); 28 | bool eventFilter(QObject *, QEvent *event); 29 | void autoSize(); 30 | void callRepaint(); 31 | void changeNodeColor(QColor c); 32 | void changeBackgroundColor(QColor c); 33 | void changeTextColor(QColor c); 34 | QColor getNodeColor() const; 35 | QColor getBackgroundColor() const; 36 | QColor getTextColor() const; 37 | 38 | signals: 39 | 40 | public slots: 41 | 42 | protected: 43 | void paintEvent(QPaintEvent *event) override; 44 | void mouseReleaseEvent(QMouseEvent *event) override; 45 | private: 46 | BinarySearchTree *bst; 47 | double scale; 48 | QPen pen; 49 | QBrush brush; 50 | QColor backgroundColor; 51 | QColor nodeColor; 52 | QColor textColor; 53 | 54 | }; 55 | 56 | #endif // RENDERAREA_H 57 | -------------------------------------------------------------------------------- /BinarySearchTree/bst_properties_window.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | */ 7 | 8 | #ifndef BST_PROPERTIES_WINDOW_H 9 | #define BST_PROPERTIES_WINDOW_H 10 | 11 | #include "binarysearchtree.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | class BST_Properties_Window : public QMainWindow 22 | { 23 | Q_OBJECT 24 | QWidget *centralWidget; 25 | public: 26 | explicit BST_Properties_Window(QWidget *parent = 0); 27 | ~BST_Properties_Window(); 28 | void show(); 29 | void closePropertyWindow(); 30 | void update(BinarySearchTree *bst); 31 | 32 | private: 33 | QVBoxLayout *labelLayout; 34 | QLabel *heightLabel; 35 | QLineEdit *heightValue; 36 | QLabel *nodeCountLabel; 37 | QLineEdit *nodeCountValue; 38 | QLabel *leafNodesLabel; 39 | QLineEdit *leafNodesValue; 40 | QLabel *internalNodesLabel; 41 | QLineEdit *internalNodesValue; 42 | QVBoxLayout *textAreaLayout; 43 | QHBoxLayout *containerLayout; 44 | QVBoxLayout *mainLayout; 45 | QTextEdit *inOrderTraversal; 46 | QTextEdit *preOrderTraversal; 47 | QTextEdit *postOrderTraversal; 48 | QTextEdit *breadthFirstTraversal; 49 | QPushButton *exitButton; 50 | 51 | private slots: 52 | void exitSlot(); 53 | }; 54 | 55 | #endif // BST_PROPERTIES_WINDOW_H 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Visualizing data in a Binary Search Tree 2 | The goal of this project is to be able to visualize data in a Binary Search Tree (BST). 3 | 4 | What's unique about BST's is that the value of the data in the left child node is less than the value in its parent node, and the value stored in the right child node is greater than the parent. This makes searching for a value more efficient than other searching methods (data stored in an array or linked list, for example). 5 | This project allows faculty to be able to graphically display Binary Search Trees while explaining them to students. 6 | 7 | ![App Image](image.png?raw=true) 8 | 9 | # Features 10 | - Load and save Binary Search Trees to a text file 11 | - Save a BST as an image file 12 | - Insert nodes graphically, after the BST has already been built (or build a tree from scratch) 13 | - Delete nodes graphically (you can not click on the node to delete it, however) 14 | - Easily view common properties of a Binary Search Tree including: height, total node count, leaf node count, and internal node count 15 | - Easily view preorder, inorder, postorder, and breadth first traversals of a BST 16 | - Zoom in and out in order to more easily view a large or small tree 17 | 18 | # Design 19 | This project is programmed completely in C++ using the Qt GUI framework. The Qt framework allows for advanced graphical programming and efficient drawing. 20 | 21 | ## Why C++ 22 | I chose to build this project in C++ for the learning experience. I have wanted to learn C++ since I became a college student so I decided I would learn by working on a project. Java may have been a better option for this sort of project since it has strong GUI support (the swing library), but again, I wanted to learn C++. C++'s use of pointers has made it an extremely effective language for this project. I have used pointers unsparingly, trying to make the program as efficiently as possible. As many of the programs functions are recursive, pointers have proven to be very effective. 23 | 24 | ## Why Qt? 25 | I chose to use Qt as the GUI framework because of its expansive documentation. There is almost no other C++ GUI library out there with documentation as good as Qt. Qt's documentation has made it a lot easier learning how to program GUI's with C++. 26 | -------------------------------------------------------------------------------- /BinarySearchTree/mainwindow.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | */ 7 | 8 | #ifndef MAINWINDOW_H 9 | #define MAINWINDOW_H 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "renderarea.h" 22 | /* 23 | * bst_properties_window.h includes: 24 | * QMainWindow, QVBoxLayout, QHBoxLayout, QScrollArea, QWidget, QLabel, QLineEdit 25 | */ 26 | #include "bst_properties_window.h" 27 | #include "bst_about_window.h" 28 | #include "binarysearchtree.h" 29 | 30 | class MainWindow : public QMainWindow 31 | { 32 | Q_OBJECT 33 | QWidget *centralWidget; 34 | 35 | public: 36 | explicit MainWindow(QWidget *parent = 0); 37 | ~MainWindow(); 38 | 39 | private: 40 | RenderArea *renderArea; 41 | QPushButton *propertyButton; 42 | QPushButton *deleteButton; 43 | QPushButton *insertButton; 44 | QPushButton *zoomInButton; 45 | QPushButton *zoomOutButton; 46 | QLineEdit *insertValueLineEdit; 47 | QLineEdit *deleteValueLineEdit; 48 | QScrollArea *treeScrollArea; 49 | QVBoxLayout *mainLayout; 50 | QLabel *statusLabel; 51 | QMenu *fileMenu; 52 | QMenu *editMenu; 53 | QAction *aboutAction; 54 | QAction *loadAction; 55 | QAction *saveAction; 56 | QAction *exitAction; 57 | QAction *resetAction; 58 | QAction *changeNodeColorAction; 59 | QAction *changeBackgroundColorAction; 60 | QAction *changeTextColorAction; 61 | BST_Properties_Window *prop; 62 | BST_About_Window *about; 63 | BinarySearchTree *bst; 64 | BinarySearchTree *getBST(); 65 | void loadSettings(); 66 | void saveSettings(); 67 | void createMenu(); 68 | void createActions(); 69 | 70 | protected: 71 | virtual void closeEvent(QCloseEvent *event); 72 | virtual void resizeEvent(QResizeEvent* event); 73 | 74 | private slots: 75 | void propertyClicked() const; 76 | void insertClicked() const; 77 | void deleteClicked() const; 78 | void zoomInClicked() const; 79 | void zoomOutClicked() const; 80 | void loadFileMenu(); 81 | void saveMenu(); 82 | void exitMenu(); 83 | void resetMenu() const; 84 | void aboutMenu() const; 85 | void changeNodeColorMenu(); 86 | void changeBackgroundColorMenu(); 87 | void changeTextColorMenu(); 88 | }; 89 | 90 | #endif // MAINWINDOW_H 91 | -------------------------------------------------------------------------------- /BinarySearchTree/bst_about_window.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | * 7 | * This class is the "About" window - it gives information about the project. 8 | */ 9 | 10 | #include "bst_about_window.h" 11 | #include 12 | 13 | BST_About_Window::BST_About_Window() 14 | { 15 | window = new QMainWindow(); 16 | window->setWindowTitle("About BST Visualization"); 17 | window->setFixedSize(QSize(350, 300)); 18 | 19 | QVBoxLayout *specifierLayout = new QVBoxLayout(); 20 | specifierLayout->addWidget(new QLabel("Date:")); 21 | specifierLayout->addWidget(new QLabel("Funding:")); 22 | specifierLayout->addWidget(new QLabel("Programmer:")); 23 | 24 | labelLayout = new QVBoxLayout; 25 | labelLayout->addWidget(new QLabel("Summer 2017")); 26 | labelLayout->addWidget(new QLabel("Ashland University Computer Science Department")); 27 | labelLayout->addWidget(new QLabel("Nathan Ahrens (nahrens@ashland.edu)")); 28 | 29 | QVBoxLayout *textAreaLayout = new QVBoxLayout(); 30 | QTextEdit *txtEdit = new QTextEdit("The Binary Search Tree Visualization program was developed by " 31 | "Nathan Ahrens who, at the time, was an Ashland University sophomore. Started as " 32 | "a fun project, the Binary Search Tree Visualization program was brought to the attention of " 33 | "Dr. Iyad Ajwa, who helped turn it into a grant-funded project. The project now has the purpose of " 34 | "helping the faculty of Ashland University better describe and display what a Binary Search Tree is to " 35 | "freshman and sophomore students.
" 36 | "For more information, view the project on GitHub:
" 37 | "https://github.com/redninja2/BinarySearchTreeGui" 38 | "

" 39 | "What about depthfirst traversal?
" 40 | "The depthfirst traversal algorithm is the same as the preorder traversal."); 41 | txtEdit->setStyleSheet("background-color: white; color: black;"); 42 | txtEdit->setReadOnly(true); 43 | textAreaLayout->addWidget(txtEdit); 44 | 45 | containerLayout = new QHBoxLayout; 46 | containerLayout->addLayout(specifierLayout); 47 | containerLayout->addLayout(labelLayout); 48 | 49 | QVBoxLayout *vContainerLayout = new QVBoxLayout(); 50 | vContainerLayout->addLayout(containerLayout); 51 | vContainerLayout->addLayout(textAreaLayout); 52 | 53 | container = new QWidget; 54 | container->setLayout(vContainerLayout); 55 | 56 | // Create the main layout and add all the widgets to it 57 | mainLayout = new QVBoxLayout; 58 | mainLayout->addWidget(container); 59 | // mainLayout->addLayout(buttonLayout); // layout for ok/close button 60 | 61 | centralWidget = new QWidget(window); 62 | centralWidget->setLayout(mainLayout); 63 | 64 | window->setCentralWidget(centralWidget); 65 | } 66 | 67 | BST_About_Window::~BST_About_Window() 68 | { 69 | delete labelLayout; 70 | delete containerLayout; 71 | delete container; 72 | delete mainLayout; 73 | delete centralWidget; 74 | delete window; 75 | } 76 | 77 | void BST_About_Window::show() 78 | { 79 | window->setVisible(true); 80 | window->activateWindow(); 81 | return; 82 | } 83 | 84 | void BST_About_Window::close() 85 | { 86 | window->close(); 87 | return; 88 | } 89 | -------------------------------------------------------------------------------- /BinarySearchTree/renderarea.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | * 7 | * This class is meant as a "canvas" for a binary search tree. 8 | * It's sole purpose is handle the drawing of the BST (given in constructor). 9 | */ 10 | 11 | #include "renderarea.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | RenderArea::RenderArea(BinarySearchTree *bst, QWidget *parent) : QWidget(parent), bst(), 19 | scale(1.0) 20 | { 21 | this->bst = bst; 22 | this->scale = 1; 23 | 24 | // Set background to white so that when the RenderArea is 25 | // saved as an image (or the RenderArea is grabbed) the 26 | // the background will be white 27 | //this->setStyleSheet("background-color: white;"); 28 | 29 | this->nodeColor = Qt::red; 30 | this->backgroundColor = Qt::white; 31 | this->textColor = Qt::black; 32 | } 33 | 34 | 35 | QSize RenderArea::sizeHint() const 36 | { 37 | return QSize(50, 50); 38 | } 39 | 40 | QSize RenderArea::minimumSizeHint() const 41 | { 42 | return QSize(50, 50); 43 | } 44 | 45 | // What to do when the render area gets repaint() called 46 | void RenderArea::paintEvent(QPaintEvent * /* event */) 47 | { 48 | // Only repaint the tree if it's not empty 49 | if (this->bst->isEmpty()) 50 | return; 51 | QPainter painter(this); 52 | 53 | painter.setRenderHint(QPainter::Antialiasing); 54 | 55 | QBrush brush; 56 | brush.setColor(this->nodeColor); 57 | brush.setStyle(Qt::SolidPattern); 58 | QPen pen; 59 | pen.setColor(this->textColor); 60 | 61 | painter.setBrush(brush); 62 | painter.setPen(pen); 63 | 64 | this->bst->draw(&painter, this->scale); 65 | 66 | // Autosize the renderArea after the tree has been drawn. 67 | this->autoSize(); 68 | } 69 | 70 | void RenderArea::changeNodeColor(QColor c) 71 | { 72 | this->nodeColor = c; 73 | } 74 | 75 | void RenderArea::changeBackgroundColor(QColor c) 76 | { 77 | // change color 78 | this->backgroundColor = c; 79 | QString style("background-color: "); 80 | style.append(c.name()); 81 | style.append(";"); 82 | this->setStyleSheet(style); 83 | } 84 | 85 | void RenderArea::changeTextColor(QColor c) 86 | { 87 | this->textColor = c; 88 | } 89 | 90 | QColor RenderArea::getNodeColor() const 91 | { 92 | return this->nodeColor; 93 | } 94 | 95 | QColor RenderArea::getBackgroundColor() const 96 | { 97 | return this->backgroundColor; 98 | } 99 | 100 | QColor RenderArea::getTextColor() const 101 | { 102 | return this->textColor; 103 | } 104 | 105 | // For outside to call - makes sure that the size is set correctly for scroll areas. 106 | void RenderArea::callRepaint() 107 | { 108 | if (this->bst->isEmpty()) 109 | return; 110 | 111 | this->scale += 0.1; 112 | this->repaint(); 113 | this->scale -= 0.1; 114 | this->repaint(); 115 | } 116 | 117 | // Increment the scale variable and redraw 118 | void RenderArea::zoomIn() { 119 | if (this->bst->isEmpty()) 120 | return; 121 | if(this->scale < 2.0){ 122 | this->scale += 0.1; 123 | this->repaint(); 124 | } 125 | } 126 | 127 | // Decrement the scale variable and redraw 128 | void RenderArea::zoomOut() { 129 | if (this->bst->isEmpty()) 130 | return; 131 | if(this->scale > 0.2) { 132 | this->scale -= 0.1; 133 | this->repaint(); 134 | } 135 | } 136 | 137 | // Handle mouse clicking that is done on the QScrollArea that should 138 | // be handled by the RenderArea (for zooming) 139 | bool RenderArea::eventFilter(QObject *, QEvent *event) 140 | { 141 | if (event->type() == QEvent::MouseButtonRelease) 142 | { 143 | QMouseEvent *mb = static_cast(event); 144 | switch(mb->button()){ 145 | case Qt::LeftButton: 146 | this->zoomIn(); 147 | break; 148 | case Qt::RightButton: 149 | this->zoomOut(); 150 | break; 151 | default: 152 | return true; 153 | } 154 | } 155 | 156 | return true; 157 | } 158 | 159 | // Auto size the render area based on the required size by the tree 160 | void RenderArea::autoSize() { 161 | QSize size(bst->getTotalX(), bst->getTotalY()); 162 | this->setMinimumSize(size); 163 | this->resize(size); 164 | } 165 | 166 | // Detect mouse release on render area 167 | void RenderArea::mouseReleaseEvent(QMouseEvent *event) 168 | { 169 | switch(event->button()){ 170 | case Qt::LeftButton: 171 | if ( event->modifiers() & Qt::ControlModifier ) 172 | { 173 | // search for a node at the provided location and delete it. Returns false if no node was found. 174 | this->bst->deleteAtLocation(event->x(), event->y()); 175 | this->repaint(); 176 | break; 177 | } 178 | 179 | this->zoomIn(); 180 | 181 | break; 182 | case Qt::RightButton: 183 | this->zoomOut(); 184 | break; 185 | default: 186 | return; 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /BinarySearchTree/bst_properties_window.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | * 7 | * This class is for the properties window - displays the various properties of the BST which is being displayed. 8 | */ 9 | 10 | #include "bst_properties_window.h" 11 | #include 12 | #include 13 | #include 14 | 15 | BST_Properties_Window::BST_Properties_Window(QWidget *parent) : QMainWindow(parent) 16 | { 17 | this->setWindowTitle("Properties"); 18 | this->setFixedSize(QSize(400, 525)); // 236 width 19 | 20 | // labelLayout is on the left side - says what the property is 21 | labelLayout = new QVBoxLayout;\ 22 | heightLabel = new QLabel("Height:"); 23 | nodeCountLabel = new QLabel("Node count:"); 24 | leafNodesLabel = new QLabel("Leaf nodes:"); 25 | internalNodesLabel = new QLabel("Internal nodes:"); 26 | heightLabel->setStyleSheet("font-size: 14px;"); 27 | nodeCountLabel->setStyleSheet("font-size: 14px;"); 28 | leafNodesLabel->setStyleSheet("font-size: 14px;"); 29 | internalNodesLabel->setStyleSheet("font-size: 14px;"); 30 | 31 | labelLayout->addWidget(heightLabel); 32 | labelLayout->addWidget(nodeCountLabel); 33 | labelLayout->addWidget(leafNodesLabel); 34 | labelLayout->addWidget(internalNodesLabel); 35 | 36 | // textAreaLayout is on the right side - displays value of property 37 | textAreaLayout = new QVBoxLayout; 38 | heightValue = new QLineEdit(""); 39 | nodeCountValue = new QLineEdit(""); 40 | leafNodesValue = new QLineEdit(""); 41 | internalNodesValue = new QLineEdit(""); 42 | heightValue->setReadOnly(true); 43 | nodeCountValue->setReadOnly(true); 44 | leafNodesValue->setReadOnly(true); 45 | internalNodesValue->setReadOnly(true); 46 | 47 | heightValue->setStyleSheet("color: black; width: 100px;" 48 | "padding-left: 10px; font-size: 14px;"); 49 | nodeCountValue->setStyleSheet("color: black; width: 100px;" 50 | "padding-left: 10px; font-size: 14px;"); 51 | leafNodesValue->setStyleSheet("color: black; width: 100px;" 52 | "padding-left: 10px; font-size: 14px;"); 53 | internalNodesValue->setStyleSheet("color: black; width: 100px;" 54 | "padding-left: 10px; font-size: 14px;"); 55 | 56 | textAreaLayout->addWidget(heightValue); 57 | textAreaLayout->addWidget(nodeCountValue); 58 | textAreaLayout->addWidget(leafNodesValue); 59 | textAreaLayout->addWidget(internalNodesValue); 60 | 61 | containerLayout = new QHBoxLayout; 62 | containerLayout->addLayout(labelLayout); 63 | containerLayout->addLayout(textAreaLayout); 64 | 65 | 66 | preOrderTraversal = new QTextEdit; 67 | inOrderTraversal = new QTextEdit; 68 | postOrderTraversal = new QTextEdit; 69 | breadthFirstTraversal = new QTextEdit; 70 | preOrderTraversal->setReadOnly(true); 71 | inOrderTraversal->setReadOnly(true); 72 | postOrderTraversal->setReadOnly(true); 73 | breadthFirstTraversal->setReadOnly(true); 74 | 75 | exitButton = new QPushButton("E&xit", this); 76 | connect(exitButton, SIGNAL(clicked()), this, SLOT(exitSlot())); 77 | 78 | QHBoxLayout *buttonLayout = new QHBoxLayout; 79 | buttonLayout->addStretch(0); 80 | buttonLayout->addWidget(exitButton); 81 | 82 | // Create the main layout and add all the widgets to it 83 | mainLayout = new QVBoxLayout; 84 | mainLayout->addLayout(containerLayout); 85 | mainLayout->addWidget(new QLabel("Preorder Traversal:")); 86 | mainLayout->addWidget(preOrderTraversal); 87 | mainLayout->addWidget(new QLabel("Inorder Traversal:")); 88 | mainLayout->addWidget(inOrderTraversal); 89 | mainLayout->addWidget(new QLabel("Postorder Traversal:")); 90 | mainLayout->addWidget(postOrderTraversal); 91 | mainLayout->addWidget(new QLabel("Breadthfirst Traversal")); 92 | mainLayout->addWidget(breadthFirstTraversal); 93 | mainLayout->addLayout(buttonLayout); // layout for ok/close button 94 | 95 | centralWidget = new QWidget(this); 96 | centralWidget->setLayout(mainLayout); 97 | 98 | this->setCentralWidget(centralWidget); 99 | } 100 | 101 | BST_Properties_Window::~BST_Properties_Window(){ 102 | delete heightLabel; 103 | delete heightValue; 104 | delete nodeCountLabel; 105 | delete nodeCountValue; 106 | delete leafNodesLabel; 107 | delete leafNodesValue; 108 | delete internalNodesLabel; 109 | delete internalNodesValue; 110 | delete labelLayout; 111 | delete textAreaLayout; 112 | delete containerLayout; 113 | delete preOrderTraversal; 114 | delete inOrderTraversal; 115 | delete postOrderTraversal; 116 | delete breadthFirstTraversal; 117 | delete exitButton; 118 | delete mainLayout; 119 | delete centralWidget; 120 | } 121 | 122 | // Close the window. 123 | void BST_Properties_Window::closePropertyWindow() 124 | { 125 | this->close(); 126 | return; 127 | } 128 | 129 | // Update the properties of the tree. 130 | void BST_Properties_Window::update(BinarySearchTree *bst) 131 | { 132 | this->heightValue->setText(QString::number(bst->getTreeHeight())); 133 | this->nodeCountValue->setText(QString::number(bst->getNodeCount())); 134 | this->leafNodesValue->setText(QString::number(bst->getLeafNodeCount())); 135 | this->internalNodesValue->setText(QString::number(bst->getNodeCount() - bst->getLeafNodeCount())); 136 | this->preOrderTraversal->setText(bst->getPreOrderTraversal()); 137 | this->inOrderTraversal->setText(bst->getInOrderTraversal()); 138 | this->postOrderTraversal->setText(bst->getPostOrderTraversal()); 139 | this->breadthFirstTraversal->setText(bst->getBreadthFirstSearch()); 140 | return; 141 | } 142 | 143 | 144 | // Set window to visible and bring it to the front. 145 | void BST_Properties_Window::show() 146 | { 147 | this->setVisible(true); 148 | this->activateWindow(); 149 | return; 150 | } 151 | 152 | void BST_Properties_Window::exitSlot() 153 | { 154 | this->close(); 155 | return; 156 | } 157 | -------------------------------------------------------------------------------- /BinarySearchTree/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | * 7 | * This class is the main window - the GUI is build in this class and any other classes involved begin from this class. 8 | */ 9 | 10 | #include "mainwindow.h" 11 | #include "bst_about_window.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | MainWindow::MainWindow(QWidget *parent) : 28 | QMainWindow(parent) 29 | { 30 | 31 | // Create default save directory 32 | QString directory = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/BSTVisualizer"; 33 | if (!QDir(directory).exists()) 34 | QDir().mkdir(directory); 35 | 36 | this->bst = this->getBST(); 37 | 38 | this->createMenu(); 39 | 40 | // Build buttons and layout for buttons on the bottom of the window 41 | propertyButton = new QPushButton("&Properties", this); 42 | deleteButton = new QPushButton("&Delete", this); 43 | insertButton = new QPushButton("Insert", this); 44 | zoomInButton = new QPushButton("Zoom &In", this); 45 | zoomOutButton = new QPushButton("Zoom &Out", this); 46 | insertValueLineEdit = new QLineEdit; 47 | deleteValueLineEdit = new QLineEdit; 48 | statusLabel = new QLabel; 49 | 50 | // Set properties of buttons 51 | propertyButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 52 | deleteButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 53 | insertButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 54 | zoomInButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 55 | zoomOutButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 56 | 57 | insertValueLineEdit->setFixedWidth(200); 58 | insertValueLineEdit->setToolTip("Enter single value or multiple values separated by space"); 59 | 60 | deleteValueLineEdit->setFixedWidth(100); 61 | deleteValueLineEdit->setToolTip("Enter value to delete"); 62 | 63 | // Connect the slots to the button signals 64 | connect(propertyButton, SIGNAL(clicked()), this, SLOT(propertyClicked())); 65 | connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteClicked())); 66 | connect(insertButton, SIGNAL(clicked()), this, SLOT(insertClicked())); 67 | connect(zoomInButton, SIGNAL(clicked()), this, SLOT(zoomInClicked())); 68 | connect(zoomOutButton, SIGNAL(clicked()), this, SLOT(zoomOutClicked())); 69 | connect(insertValueLineEdit, SIGNAL(returnPressed()), this, SLOT(insertClicked())); 70 | connect(deleteValueLineEdit, SIGNAL(returnPressed()), this, SLOT(deleteClicked())); 71 | 72 | // Create button layout and add buttons 73 | QHBoxLayout *buttonLayout = new QHBoxLayout; 74 | buttonLayout->addWidget(propertyButton); 75 | buttonLayout->addWidget(deleteButton); 76 | buttonLayout->addWidget(deleteValueLineEdit); 77 | buttonLayout->addWidget(insertButton); 78 | buttonLayout->addWidget(insertValueLineEdit); 79 | buttonLayout->addSpacing(25); 80 | buttonLayout->addWidget(statusLabel); 81 | buttonLayout->addStretch(0); 82 | buttonLayout->addWidget(zoomInButton); 83 | buttonLayout->addWidget(zoomOutButton); 84 | 85 | // Create the render area (canvas for drawing the BST) 86 | renderArea = new RenderArea(this->bst); 87 | 88 | treeScrollArea = new QScrollArea; 89 | treeScrollArea->setWidget(renderArea); 90 | 91 | // Pass any events that happen on the scroll area to the 92 | // render area (specifically clicking, so that renderArea 93 | // can zoom in/out when clicks happen 94 | treeScrollArea->installEventFilter(renderArea); 95 | 96 | // Create the main layout and add all the widgets to it 97 | mainLayout = new QVBoxLayout; 98 | mainLayout->addWidget(treeScrollArea); 99 | mainLayout->addLayout(buttonLayout); 100 | 101 | // Build the main window 102 | centralWidget = new QWidget(this); 103 | centralWidget->setLayout(mainLayout); 104 | this->setCentralWidget(centralWidget); 105 | this->setMinimumHeight(400); 106 | this->setWindowTitle("Binary Search Tree Visualization"); 107 | //this->showMaximized(); 108 | 109 | // Create secondary windows (but do not display them) 110 | prop = new BST_Properties_Window(); 111 | about = new BST_About_Window(); 112 | 113 | // must show window before loading settings 114 | this->show(); 115 | this->loadSettings(); 116 | 117 | } 118 | 119 | void MainWindow::resizeEvent(QResizeEvent* event) 120 | { 121 | QMainWindow::resizeEvent(event); 122 | this->renderArea->callRepaint(); 123 | } 124 | 125 | MainWindow::~MainWindow() 126 | { 127 | delete renderArea; 128 | delete propertyButton; 129 | delete deleteButton; 130 | delete insertButton; 131 | delete zoomInButton; 132 | delete zoomOutButton; 133 | delete treeScrollArea; 134 | delete prop; 135 | delete about; 136 | delete bst; 137 | delete centralWidget; 138 | } 139 | 140 | void MainWindow::createMenu() 141 | { 142 | this->createActions(); 143 | 144 | fileMenu = this->menuBar()->addMenu(tr("&File")); 145 | fileMenu->addAction(loadAction); 146 | fileMenu->addAction(saveAction); 147 | fileMenu->addAction(exitAction); 148 | 149 | editMenu = this->menuBar()->addMenu(tr("&Edit")); 150 | editMenu->addAction(resetAction); 151 | editMenu->addAction(changeNodeColorAction); 152 | editMenu->addAction(changeBackgroundColorAction); 153 | editMenu->addAction(changeTextColorAction); 154 | 155 | this->menuBar()->addAction(aboutAction); 156 | } 157 | 158 | void MainWindow::createActions() 159 | { 160 | aboutAction = new QAction(tr("&About"), this); 161 | aboutAction->setStatusTip("About Binary Search Tree Visualization"); 162 | connect(aboutAction, &QAction::triggered, this, &MainWindow::aboutMenu); 163 | 164 | loadAction = new QAction(tr("&Load"), this); 165 | loadAction->setStatusTip("Load a BST from a file"); 166 | connect(loadAction, &QAction::triggered, this, &MainWindow::loadFileMenu); 167 | 168 | saveAction = new QAction(tr("&Save"), this); 169 | saveAction->setStatusTip("Save a BST to a file"); 170 | connect(saveAction, &QAction::triggered, this, &MainWindow::saveMenu); 171 | 172 | exitAction = new QAction(tr("&Exit"), this); 173 | exitAction->setStatusTip("Exit the application"); 174 | connect(exitAction, &QAction::triggered, this, &MainWindow::exitMenu); 175 | 176 | resetAction = new QAction(tr("&Reset"), this); 177 | resetAction->setStatusTip("Reset the BST to be empty"); 178 | connect(resetAction, &QAction::triggered, this, &MainWindow::resetMenu); 179 | 180 | changeNodeColorAction = new QAction(tr("&Node color"), this); 181 | changeNodeColorAction->setStatusTip("Change Node Color"); 182 | connect(changeNodeColorAction, &QAction::triggered, this, &MainWindow::changeNodeColorMenu); 183 | 184 | changeBackgroundColorAction = new QAction(tr("&Background color"), this); 185 | changeBackgroundColorAction->setStatusTip("Change Background Color"); 186 | connect(changeBackgroundColorAction, &QAction::triggered, this, &MainWindow::changeBackgroundColorMenu); 187 | 188 | changeTextColorAction = new QAction(tr("Node &text color"), this); 189 | changeTextColorAction->setStatusTip("Change Node Text Color"); 190 | connect(changeTextColorAction, &QAction::triggered, this, &MainWindow::changeTextColorMenu); 191 | 192 | } 193 | 194 | void MainWindow::closeEvent(QCloseEvent *event) 195 | { 196 | 197 | // Save BST before closing 198 | QString fileName = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/BSTVisualizer/last_bst.txt"; 199 | 200 | QString text = bst->getPreOrderTraversal(); 201 | QFile file(fileName); 202 | if (file.open(QIODevice::WriteOnly | QIODevice::Text)) 203 | { 204 | QTextStream writer(&file); 205 | writer << text; 206 | writer.flush(); 207 | file.close(); 208 | } 209 | this->saveSettings(); 210 | 211 | prop->closePropertyWindow(); // close property window 212 | about->close(); // close about window 213 | event->setAccepted(true); // set whether to close application or not 214 | return; 215 | } 216 | 217 | // Slot for property button 218 | void MainWindow::propertyClicked() const 219 | { 220 | // show and update the properties gui 221 | prop->show(); 222 | prop->update(this->bst); 223 | return; 224 | } 225 | 226 | // Slot for delete button 227 | void MainWindow::deleteClicked() const { 228 | QString value = deleteValueLineEdit->text(); 229 | 230 | if(!this->bst->deleteItem(value.toInt())) 231 | this->statusLabel->setText("Value is not in tree..."); 232 | else 233 | this->statusLabel->setText("Value deleted."); 234 | 235 | this->renderArea->repaint(); // repaint to show changes to tree 236 | this->deleteValueLineEdit->setText(""); // clear text box 237 | return; 238 | } 239 | 240 | // Slot for insert button 241 | void MainWindow::insertClicked() const 242 | { 243 | // Get entire line of text and iterate through the list of 244 | // values separated by whitespace - inserting all the values 245 | QString values = insertValueLineEdit->text(); 246 | QStringList valueList = values.split(QRegExp("\\s+"), QString::SkipEmptyParts); 247 | QStringListIterator iterator(valueList); 248 | 249 | while (iterator.hasNext()) 250 | { 251 | if(!this->bst->insert(iterator.next().toInt())) // inserts 0 if text isn't an int 252 | this->statusLabel->setText("Duplicate valaue..."); 253 | else 254 | this->statusLabel->setText("Value inserted..."); 255 | } 256 | this->renderArea->repaint(); // repaint to show changes to tree 257 | insertValueLineEdit->setText(""); // clear text box 258 | return; 259 | } 260 | 261 | // Slot for zoom in button 262 | void MainWindow::zoomInClicked() const { 263 | this->statusLabel->setText(""); 264 | renderArea->zoomIn(); 265 | return; 266 | } 267 | 268 | // Slot for zoom out button 269 | void MainWindow::zoomOutClicked() const { 270 | this->statusLabel->setText(""); 271 | renderArea->zoomOut(); 272 | return; 273 | } 274 | 275 | // Slot for load in the file menu 276 | void MainWindow::loadFileMenu() 277 | { 278 | QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"), 279 | QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/BSTVisualizer", 280 | tr("Text files (*.txt)")); 281 | 282 | QString text; 283 | QFile file(fileName); 284 | if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) 285 | { 286 | this->statusLabel->setText("Could not open file!"); 287 | return; 288 | } 289 | 290 | this->bst->resetTree(); 291 | 292 | QTextStream reader(&file); 293 | 294 | while (!reader.atEnd()) 295 | { 296 | reader >> text; 297 | if(text != " " && text != "") 298 | this->bst->insert(text.toInt()); 299 | } 300 | file.close(); 301 | 302 | this->renderArea->repaint(); 303 | 304 | this->statusLabel->setText("File successfully opened!"); 305 | return; 306 | } 307 | 308 | // Slot for save action in menu 309 | void MainWindow::saveMenu() 310 | { 311 | QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), 312 | QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/BSTVisualizer", 313 | tr("Text files (*.txt);;Images (*.png *.jpg)")); 314 | 315 | if (QFileInfo(fileName).suffix() == "txt") 316 | { 317 | QString text = bst->getPreOrderTraversal(); 318 | QFile file(fileName); 319 | if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) 320 | { 321 | this->statusLabel->setText("File was not saved!"); 322 | return; 323 | } 324 | QTextStream writer(&file); 325 | writer << text; 326 | writer.flush(); 327 | file.close(); 328 | this->statusLabel->setText("File successfully saved!"); 329 | return; 330 | } 331 | 332 | // if not txt, save as image 333 | if(!this->renderArea->grab().save(fileName)) 334 | { 335 | this->statusLabel->setText("Image was not saved..."); 336 | return; 337 | } 338 | this->statusLabel->setText("Image saved..."); 339 | 340 | return; 341 | } 342 | 343 | // Slot for exit action in menu 344 | void MainWindow::exitMenu() 345 | { 346 | this->close(); 347 | return; 348 | } 349 | 350 | // Slot for reset action in menu 351 | void MainWindow::resetMenu() const 352 | { 353 | this->statusLabel->setText("Reset tree..."); 354 | this->bst->resetTree(); 355 | this->renderArea->repaint(); 356 | return; 357 | } 358 | 359 | // Slot for about action in menu 360 | void MainWindow::aboutMenu() const 361 | { 362 | about->show(); 363 | return; 364 | } 365 | 366 | // Slot for changing node color in menu 367 | void MainWindow::changeNodeColorMenu() 368 | { 369 | QColor color = QColorDialog::getColor(Qt::black, this); 370 | if (color.isValid()) 371 | { 372 | // change color 373 | this->renderArea->changeNodeColor(color); 374 | this->renderArea->repaint(); 375 | } 376 | return; 377 | } 378 | 379 | // Slot for changing background color in menu 380 | void MainWindow::changeBackgroundColorMenu() 381 | { 382 | QColor color = QColorDialog::getColor(Qt::black, this); 383 | if (color.isValid()) 384 | { 385 | // change color 386 | QPalette pal(this->treeScrollArea->palette()); 387 | pal.setColor(QPalette::Window, color); 388 | this->treeScrollArea->setPalette(pal); 389 | this->renderArea->changeBackgroundColor(color); 390 | } 391 | return; 392 | } 393 | 394 | 395 | // Slot for changing background color in menu 396 | void MainWindow::changeTextColorMenu() 397 | { 398 | QColor color = QColorDialog::getColor(Qt::black, this); 399 | if (color.isValid()) 400 | { 401 | // change color 402 | this->renderArea->changeTextColor(color); 403 | this->renderArea->repaint(); 404 | } 405 | return; 406 | } 407 | 408 | BinarySearchTree* MainWindow::getBST() 409 | { 410 | 411 | BinarySearchTree *bst = new BinarySearchTree; 412 | 413 | QString fileName = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/BSTVisualizer/last_bst.txt"; 414 | 415 | QString text; 416 | QFile file(fileName); 417 | 418 | // If the file doesn't exist or if it can't open, return an empty bst 419 | if (!file.exists() || !file.open(QIODevice::ReadOnly | QIODevice::Text)) 420 | { 421 | return bst; 422 | } 423 | 424 | QTextStream reader(&file); 425 | 426 | while (!reader.atEnd()) 427 | { 428 | reader >> text; 429 | if (text != " " && text != "") 430 | bst->insert(text.toInt()); 431 | } 432 | 433 | file.close(); 434 | return bst; 435 | } 436 | 437 | void MainWindow::saveSettings() 438 | { 439 | QString fileName = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/BSTVisualizer/settings.txt"; 440 | QString text; 441 | text = "text-color:" + this->renderArea->getTextColor().name() + "\n"; 442 | text += "background-color:" + this->renderArea->getBackgroundColor().name() + "\n"; 443 | text += "node-color:" + this->renderArea->getNodeColor().name() + "\n"; 444 | 445 | QFile file(fileName); 446 | if (file.open(QIODevice::WriteOnly | QIODevice::Text)) 447 | { 448 | QTextStream writer(&file); 449 | writer << text; 450 | writer.flush(); 451 | file.close(); 452 | } 453 | return; 454 | } 455 | 456 | void MainWindow::loadSettings() 457 | { 458 | QString fileName = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation) + "/BSTVisualizer/settings.txt"; 459 | QString text; 460 | QFile file(fileName); 461 | 462 | // If the file doesn't exist or if it can't open, return 463 | if (!file.exists() || !file.open(QIODevice::ReadOnly | QIODevice::Text)) 464 | { 465 | return; 466 | } 467 | 468 | QTextStream reader(&file); 469 | 470 | while (!reader.atEnd()) 471 | { 472 | reader >> text; 473 | QStringList list = text.split(":"); 474 | QColor c(list.value(1)); 475 | if (!c.isValid()){ 476 | file.close(); 477 | return; 478 | } 479 | if (list.value(0) == QString("background-color")){ 480 | QPalette pal(this->treeScrollArea->palette()); 481 | pal.setColor(QPalette::Window, c); 482 | this->treeScrollArea->setPalette(pal); 483 | this->renderArea->changeBackgroundColor(c); 484 | 485 | }else if (list.value(0) == QString("node-color")){ 486 | this->renderArea->changeNodeColor(c); 487 | 488 | }else if (list.value(0) == QString("text-color")){ 489 | this->renderArea->changeTextColor(c); 490 | } 491 | } 492 | file.close(); 493 | } 494 | -------------------------------------------------------------------------------- /BinarySearchTree/binarysearchtree.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Binary Search Tree Visualization 3 | * Ashland University 4 | * Nathan Ahrens 5 | * Summer 2017 6 | * 7 | * Creation process: 8 | * This class is the BST and Node. This was built using a standard BST (from a C++ book), 9 | * but has been heavily modified to fit the needs of visualizing a BST. Some functions 10 | * have also been created to assist with drawing the BST. 11 | * 12 | * Drawing the BST: 13 | * In order to draw the BST, you must pass a QPainter object to the public draw() function. 14 | * This BST will take care of everything else - provided that the QWidget you are drawing on 15 | * is large enough. 16 | * 17 | * Sizing the "canvas" for the BST to fit: 18 | * This BST class also contains functions to assist with the sizing of the QWidget "canvas". 19 | * The functions "getTotalY()" and "getTotalX()" return the pixel dimensions required to 20 | * fully display the BST. 21 | * Then, on the canvas QWidget, you can just use setMinimumSize to ensure that the QWidget is 22 | * sized appropriately. 23 | */ 24 | 25 | 26 | #ifndef BINARYSEARCHTREE_H_ 27 | #define BINARYSEARCHTREE_H_ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | 36 | template class BinarySearchTree; 37 | 38 | // Class Node 39 | template 40 | class Node 41 | { 42 | friend class BinarySearchTree ; 43 | public: 44 | Node(const T &); // Constructor 45 | private: 46 | T data; 47 | int x; 48 | Node *leftChild; 49 | Node *rightChild; 50 | Node *parent; 51 | }; 52 | 53 | // Class BinarySearchTree 54 | template 55 | class BinarySearchTree 56 | { 57 | public: 58 | BinarySearchTree(); 59 | ~BinarySearchTree(); 60 | bool isEmpty() const; 61 | bool insert(const T &); // return false if duplicate... 62 | void resetTree(); 63 | void preOrderTraversal() const; 64 | void inOrderTraversal() const; 65 | void postOrderTraversal() const; 66 | QString getPreOrderTraversal() const; 67 | QString getInOrderTraversal() const; 68 | QString getPostOrderTraversal() const; 69 | QString getBreadthFirstSearch(); 70 | int getNodeCount() const; 71 | int getLeafNodeCount() const; 72 | int getTreeHeight() const; 73 | bool deleteItem(T); 74 | bool find(T) const; 75 | void draw(QPainter *painter, double &scale); 76 | int getTotalY() const; 77 | int getTotalX() const; 78 | bool deleteAtLocation(int x, int y); 79 | private: 80 | QPainter *painter; 81 | Node *root; 82 | int yspace; 83 | int xspace; 84 | int nodeRadius; 85 | double scale; 86 | int max(int a, int b) const; 87 | void recursivePreOrder(const Node *) const; 88 | void recursiveInOrder(const Node *) const; 89 | void recursivePostOrder(const Node *) const; 90 | int recursiveCountNodes(const Node *) const; 91 | int recursiveCountLeafNodes(const Node *) const; 92 | int recursiveComputeHeightOfTree(const Node *) const; 93 | void recursiveDeleteNodes(const Node *); 94 | void recursiveDraw(Node *node); 95 | Node* getLeftmostNode(Node *node) const; 96 | int getNodeLevel(Node *node); 97 | int getPxLocOfLeftTree(const Node *node); 98 | int getPxLocOfAncestor(const Node *node); 99 | void resetNodePosition(Node *node); 100 | bool recursiveDeleteAtLocation(Node *node, int x, int y); 101 | }; 102 | 103 | // Node constructor 104 | template 105 | Node::Node(const T &info) : 106 | data(info), x(0), leftChild(0), rightChild(0), parent(0) 107 | { 108 | // empty constructor 109 | } 110 | 111 | template 112 | int BinarySearchTree::max(int a, int b) const 113 | { 114 | return a > b ? a : b; 115 | } 116 | 117 | template 118 | BinarySearchTree::BinarySearchTree() : 119 | root(0), scale(1) 120 | { 121 | 122 | } 123 | 124 | template 125 | BinarySearchTree::~BinarySearchTree() 126 | { 127 | // post order traverse to delete all nodes 128 | recursiveDeleteNodes(root); 129 | this->root = 0; 130 | } 131 | 132 | // Delete a node at a given x, y location 133 | template 134 | bool BinarySearchTree::recursiveDeleteAtLocation(Node *node, int x, int y) 135 | { 136 | if (node == 0) 137 | return false; 138 | 139 | 140 | if (recursiveDeleteAtLocation(node->leftChild, x, y) == true) 141 | return true; 142 | 143 | int level = getNodeLevel(node); 144 | int nodey = (level * nodeRadius * 2 + yspace * (level-1)) - nodeRadius; 145 | int nodex = node->x - nodeRadius; 146 | 147 | // correct x, check y 148 | if (nodex <= x && x <=nodex + nodeRadius*2 ) 149 | { 150 | // correct y, delete 151 | if (nodey <= y && y <= nodey + nodeRadius*2) 152 | { 153 | deleteItem(node->data); 154 | return true; 155 | } 156 | } 157 | return recursiveDeleteAtLocation(node->rightChild, x, y); 158 | } 159 | 160 | template 161 | bool BinarySearchTree::deleteAtLocation(int x, int y) 162 | { 163 | return recursiveDeleteAtLocation(root, x, y); 164 | } 165 | 166 | template 167 | bool BinarySearchTree::isEmpty() const 168 | { 169 | return root == 0; 170 | } 171 | 172 | template 173 | bool BinarySearchTree::insert(const T &item) 174 | { 175 | // return false if duplicate... 176 | 177 | Node *newNode = new Node(item); 178 | 179 | if (this->isEmpty()) 180 | { 181 | this->root = newNode; 182 | return true; // successful insertion 183 | } 184 | 185 | Node *currentNode = root; 186 | Node *trailCurrentNode = root; 187 | 188 | while (currentNode != 0) 189 | { 190 | if (currentNode->data < item) 191 | { 192 | trailCurrentNode = currentNode; 193 | currentNode = currentNode->rightChild; 194 | } 195 | else if (currentNode->data > item) 196 | { 197 | trailCurrentNode = currentNode; 198 | currentNode = currentNode->leftChild; 199 | } 200 | else 201 | { 202 | std::cout << "Duplicate value: " << currentNode->data << std::endl; 203 | return false; // duplicate 204 | } 205 | } 206 | 207 | if (trailCurrentNode->data < item) 208 | trailCurrentNode->rightChild = newNode; 209 | else 210 | trailCurrentNode->leftChild = newNode; 211 | 212 | newNode->parent = trailCurrentNode; 213 | 214 | return true; // Successful 215 | } 216 | 217 | template 218 | void BinarySearchTree::resetTree() 219 | { 220 | // delete each node ( just like in destructor ) 221 | recursiveDeleteNodes(root); 222 | root = 0; 223 | return; 224 | } 225 | 226 | template 227 | void BinarySearchTree::preOrderTraversal() const 228 | { 229 | recursivePreOrder(root); 230 | return; 231 | } 232 | 233 | template 234 | void BinarySearchTree::inOrderTraversal() const 235 | { 236 | recursiveInOrder(root); 237 | return; 238 | } 239 | 240 | template 241 | void BinarySearchTree::postOrderTraversal() const 242 | { 243 | recursivePostOrder(root); 244 | return; 245 | } 246 | 247 | template 248 | int BinarySearchTree::getNodeCount() const 249 | { 250 | if (this->isEmpty()) 251 | return 0; 252 | return recursiveCountNodes(root); 253 | } 254 | 255 | template 256 | int BinarySearchTree::getLeafNodeCount() const 257 | { 258 | if (this->isEmpty()) 259 | return 0; 260 | return recursiveCountLeafNodes(root); 261 | } 262 | 263 | template 264 | int BinarySearchTree::getTreeHeight() const 265 | { 266 | if (this->isEmpty()) 267 | return 0; 268 | return recursiveComputeHeightOfTree(root); 269 | } 270 | 271 | template 272 | bool BinarySearchTree::deleteItem(T item) 273 | { 274 | if (this->isEmpty()) 275 | return false; 276 | 277 | bool found = false; 278 | Node *currentNode = root; 279 | Node *trailCurrentNode = root; 280 | 281 | while (!found && currentNode != 0) 282 | { 283 | if (currentNode->data == item) 284 | found = true; 285 | else if (currentNode->data < item) 286 | { 287 | trailCurrentNode = currentNode; 288 | currentNode = currentNode->rightChild; 289 | } 290 | else 291 | { 292 | trailCurrentNode = currentNode; 293 | currentNode = currentNode->leftChild; 294 | } 295 | } 296 | 297 | if (!found) 298 | return found; 299 | 300 | if (currentNode->leftChild == 0 && currentNode->rightChild == 0) 301 | { 302 | if (currentNode == root) 303 | { 304 | delete root; 305 | root = 0; 306 | } 307 | else if (trailCurrentNode->data < item) 308 | { 309 | delete trailCurrentNode->rightChild; 310 | trailCurrentNode->rightChild = 0; 311 | } 312 | else 313 | { 314 | delete trailCurrentNode->leftChild; 315 | trailCurrentNode->leftChild = 0; 316 | } 317 | return found; 318 | } 319 | 320 | // NO LEFT, YES RIGHT 321 | if (currentNode->leftChild == 0 && currentNode->rightChild != 0) 322 | { 323 | // ROOT WITH RIGHT 324 | if (currentNode == root) 325 | { 326 | Node *tempPtr = root; 327 | root->rightChild->parent = 0; 328 | root = root->rightChild; 329 | delete tempPtr; 330 | return found; 331 | } 332 | 333 | if (trailCurrentNode->data < item) 334 | { 335 | Node *tempPtr = trailCurrentNode->rightChild; 336 | trailCurrentNode->rightChild = currentNode->rightChild; 337 | currentNode->rightChild->parent = trailCurrentNode; 338 | delete tempPtr; 339 | } 340 | else 341 | { 342 | Node *tempPtr = trailCurrentNode->leftChild; 343 | trailCurrentNode->leftChild = currentNode->rightChild; 344 | currentNode->rightChild->parent = trailCurrentNode; 345 | delete tempPtr; 346 | } 347 | 348 | return found; 349 | } 350 | // LEFT CHILD, NO RIGHT CHILD 351 | if (currentNode->leftChild != 0 && currentNode->rightChild == 0) 352 | { 353 | // Root with left child 354 | if (currentNode == root) 355 | { 356 | Node *tempPtr = root; 357 | root = root->leftChild; 358 | root->parent = 0; 359 | delete tempPtr; 360 | } 361 | if (trailCurrentNode->data < item) 362 | { 363 | Node *tempPtr = trailCurrentNode->rightChild; 364 | trailCurrentNode->rightChild = currentNode->leftChild; 365 | currentNode->leftChild->parent = trailCurrentNode; 366 | delete tempPtr; 367 | } 368 | else 369 | { 370 | Node *tempPtr = trailCurrentNode->leftChild; 371 | trailCurrentNode->leftChild = currentNode->leftChild; 372 | currentNode->leftChild->parent = trailCurrentNode; 373 | delete tempPtr; 374 | } 375 | return found; 376 | } 377 | 378 | //if the node has both children 379 | //find the succesor of the node to be deleted, replace the value and delete the succesor 380 | if (currentNode->leftChild != 0 && currentNode->rightChild != 0) 381 | { 382 | //if the leftchildren of the node we want to delete is a leaf, it means the children itself is the succesor 383 | Node *ptr = currentNode; 384 | trailCurrentNode = currentNode; 385 | ptr = ptr->leftChild; 386 | if(ptr->rightChild==NULL) 387 | { 388 | if(ptr->leftChild==NULL) 389 | { 390 | currentNode->data=ptr->data; 391 | currentNode->leftChild=NULL; 392 | delete ptr; 393 | } 394 | else 395 | { 396 | currentNode->data=ptr->data; 397 | currentNode->leftChild=ptr->leftChild; 398 | ptr->leftChild->parent=currentNode; 399 | delete ptr; 400 | 401 | } 402 | 403 | } 404 | else 405 | { 406 | //else we look for the succesor in the subtree 407 | 408 | while (ptr->rightChild!=0) 409 | ptr=ptr->rightChild; 410 | trailCurrentNode=ptr->parent; 411 | trailCurrentNode->rightChild=NULL; 412 | currentNode->data=ptr->data; 413 | delete ptr; 414 | } 415 | } 416 | return found; 417 | } 418 | 419 | template 420 | bool BinarySearchTree::find(T item) const 421 | { 422 | if (this->isEmpty()) 423 | return false; 424 | 425 | Node *currentNode = root; 426 | while (currentNode != 0) 427 | { 428 | if (currentNode->data == item) 429 | return true; 430 | else if (currentNode->data < item) 431 | currentNode = currentNode->rightChild; 432 | else 433 | currentNode = currentNode->leftChild; 434 | } 435 | 436 | return false; 437 | } 438 | 439 | // private 440 | template 441 | void BinarySearchTree::recursivePreOrder(const Node *node) const 442 | { 443 | if (node == 0) 444 | return; 445 | std::cout << node->data << " "; 446 | recursivePreOrder(node->leftChild); 447 | recursivePreOrder(node->rightChild); 448 | 449 | return; 450 | } 451 | 452 | template 453 | void BinarySearchTree::recursiveInOrder(const Node *node) const 454 | { 455 | if (node == 0) 456 | return; 457 | recursiveInOrder(node->leftChild); 458 | std::cout << node->data << " "; 459 | recursiveInOrder(node->rightChild); 460 | 461 | return; 462 | } 463 | 464 | template 465 | void BinarySearchTree::recursivePostOrder(const Node *node) const 466 | { 467 | if (node == 0) 468 | return; 469 | recursivePostOrder(node->leftChild); 470 | recursivePostOrder(node->rightChild); 471 | std::cout << node->data << " "; 472 | 473 | return; 474 | } 475 | 476 | template 477 | QString BinarySearchTree::getPreOrderTraversal() const 478 | { 479 | QStack*> stack; 480 | QString traversal; 481 | Node *root = this->root; 482 | while (true) { 483 | // Go to the left extreme insert all the elements to stack, add to string as encountered 484 | while (root != 0) { 485 | traversal.append(QString::number(root->data) + " "); 486 | stack.push(root); 487 | root = root->leftChild; 488 | } 489 | // check if Stack is empty, if yes, exit from everywhere 490 | if (stack.isEmpty()) { 491 | return traversal; 492 | } 493 | // pop the element from the stack, add the nodes at 494 | // the right to the Stack 495 | root = stack.pop(); 496 | root = root->rightChild; 497 | } 498 | } 499 | 500 | template 501 | QString BinarySearchTree::getInOrderTraversal() const 502 | { 503 | QStack*> stack; 504 | QString traversal; 505 | Node *root = this->root; 506 | while (true) { 507 | // Go to the left extreme insert all the elements to stack 508 | while (root != 0) { 509 | stack.push(root); 510 | root = root->leftChild; 511 | } 512 | // check if Stack is empty, if yes, exit from everywhere 513 | if (stack.isEmpty()) { 514 | return traversal; 515 | } 516 | // pop the element from the stack , print it and add the nodes at 517 | // the right to the Stack 518 | root = stack.pop(); 519 | traversal.append(QString::number(root->data) + " "); 520 | root = root->rightChild; 521 | } 522 | } 523 | 524 | template 525 | QString BinarySearchTree::getPostOrderTraversal() const 526 | { 527 | 528 | if (this->isEmpty()) 529 | return QString(""); 530 | QStack*> stack1; 531 | QStack*> stack2; 532 | QString traversal; 533 | Node *root = this->root; 534 | stack1.push(root); 535 | while (!stack1.isEmpty()) 536 | { 537 | // Take out the root and insert into stack 2 538 | Node *temp = stack1.pop(); 539 | stack2.push(temp); 540 | 541 | // now we have the root, push the left and right child of root into the first stack 542 | if (temp->leftChild != 0) 543 | stack1.push(temp->leftChild); 544 | if (temp->rightChild != 0) 545 | stack1.push(temp->rightChild); 546 | } 547 | 548 | while(!stack2.isEmpty()) 549 | traversal.append(QString::number(stack2.pop()->data) + " "); 550 | 551 | return traversal; 552 | } 553 | /* 554 | * Return a string of the breadth first traversal. 555 | */ 556 | template 557 | QString BinarySearchTree::getBreadthFirstSearch() 558 | { 559 | QString traversal(""); 560 | 561 | /* Points to node we are processing. */ 562 | Node *traverse; 563 | 564 | if (this->root == 0) 565 | return traversal; /* Nothing to traverse. */ 566 | 567 | /* Create a queue to hold node pointers. */ 568 | QQueue*> ptrQueue; 569 | 570 | /* 571 | * Gotta put something in the queue initially, 572 | * so that we enter the body of the loop. 573 | */ 574 | ptrQueue.enqueue(this->root); 575 | 576 | while (!ptrQueue.isEmpty()) { 577 | traverse = ptrQueue.dequeue(); 578 | 579 | //Visit the node pointed to by traverse. 580 | traversal.append(QString::number(traverse->data) + " "); 581 | 582 | /* 583 | * If there is a left child, add it 584 | * for later processing. 585 | */ 586 | if (traverse->leftChild != 0) 587 | ptrQueue.enqueue(traverse->leftChild); 588 | 589 | /* 590 | * If there is a right child, add it 591 | * for later processing. 592 | */ 593 | if (traverse->rightChild != 0) 594 | ptrQueue.enqueue(traverse->rightChild); 595 | } 596 | 597 | return traversal; 598 | } 599 | 600 | 601 | template 602 | int BinarySearchTree::recursiveCountNodes(const Node *node) const 603 | { 604 | if (node == 0) 605 | return 0; 606 | return (1 + recursiveCountNodes(node->leftChild) 607 | + recursiveCountNodes(node->rightChild)); 608 | 609 | } 610 | 611 | template 612 | int BinarySearchTree::recursiveCountLeafNodes(const Node *node) const 613 | { 614 | if (node == 0) 615 | return 0; 616 | if (node->leftChild == 0 && node->rightChild == 0) 617 | return 1; 618 | 619 | return (recursiveCountLeafNodes(node->leftChild) 620 | + recursiveCountLeafNodes(node->rightChild)); 621 | } 622 | 623 | template 624 | int BinarySearchTree::recursiveComputeHeightOfTree(const Node *node) const 625 | { 626 | if (node == 0 || (node->leftChild == 0 && node->rightChild == 0)) 627 | return 0; 628 | 629 | return 1 630 | + max(recursiveComputeHeightOfTree(node->leftChild), 631 | recursiveComputeHeightOfTree(node->rightChild)); 632 | } 633 | 634 | template 635 | void BinarySearchTree::recursiveDeleteNodes(const Node *node) 636 | { 637 | if (node == 0) 638 | return; 639 | recursiveDeleteNodes(node->leftChild); 640 | recursiveDeleteNodes(node->rightChild); 641 | delete node; 642 | 643 | return; 644 | } 645 | 646 | // Set all nodes' x value to 0 in preperation for redrawing with a scale multiplier applied. 647 | template 648 | void BinarySearchTree::resetNodePosition(Node *node) 649 | { 650 | if (node == 0) 651 | return; 652 | resetNodePosition(node->leftChild); 653 | node->x = 0; 654 | resetNodePosition(node->rightChild); 655 | 656 | return; 657 | } 658 | 659 | 660 | template 661 | void BinarySearchTree::draw(QPainter *painter, double &scale) 662 | { 663 | if(this->root == 0) 664 | return; 665 | // Set properties of the painter for drawing the tree 666 | this->painter = painter; 667 | this->painter->setFont(QFont("Times", 12 * scale, QFont::Normal)); 668 | 669 | // Set variables for drawing the tree 670 | this->scale = scale; 671 | this->nodeRadius = 20 * scale; 672 | this->xspace = nodeRadius; 673 | this->yspace = nodeRadius * 5; 674 | 675 | // Before drawing, must make sure that all nodes have x = 0 since in recursiveDraw() we check value of x on some nodes. 676 | resetNodePosition(root); 677 | 678 | // first node drawn (leftmost node) needs to have a static, predefined 679 | // location for the rest of the tree to be based off. 680 | Node *leftmost = getLeftmostNode(root); 681 | leftmost->x = nodeRadius * 2; 682 | 683 | // Draw the tree 684 | this->recursiveDraw(root); 685 | 686 | return; 687 | } 688 | 689 | 690 | 691 | // Recursively get the leftmost node 692 | template 693 | Node* BinarySearchTree::getLeftmostNode(Node *node) const 694 | { 695 | if (node->leftChild == 0) 696 | return node; 697 | return getLeftmostNode(node->leftChild); 698 | } 699 | 700 | 701 | 702 | // Get the level of the node by tracing back its parents 703 | template 704 | int BinarySearchTree::getNodeLevel(Node *node) 705 | { 706 | int level = 1; 707 | Node *current = node; 708 | 709 | while(current->parent != 0){ 710 | current = current->parent; 711 | ++level; 712 | } 713 | return level; 714 | } 715 | 716 | 717 | 718 | // Calculate where the rightmost node is drawn of a left subtree 719 | template 720 | int BinarySearchTree::getPxLocOfLeftTree(const Node *node) 721 | { 722 | if(node->rightChild == 0){ 723 | return node->x; 724 | } 725 | return getPxLocOfLeftTree(node->rightChild); 726 | } 727 | 728 | 729 | 730 | // Calculate where the ancestor of a node is so that the leftmost node of the right 731 | // subtree can be drawn just to the right of the ancestor 732 | template 733 | int BinarySearchTree::getPxLocOfAncestor(const Node *node) 734 | { 735 | // All ancestor's node->x will be 0 unless it has already been drawn - 736 | // find the ancestor who's x != 0 737 | Node *currentNode = node->parent; 738 | 739 | while(currentNode->x == 0) 740 | currentNode = currentNode->parent; 741 | 742 | return currentNode->x; 743 | } 744 | 745 | template 746 | int BinarySearchTree::getTotalY() const 747 | { 748 | int level = getTreeHeight() + 1; 749 | return (level * nodeRadius * 2 + yspace * (level-1)) + nodeRadius * 2; 750 | } 751 | 752 | template 753 | int BinarySearchTree::getTotalX() const 754 | { 755 | if (this->root == 0) 756 | return nodeRadius*3; 757 | 758 | Node *current = root; 759 | while (current->rightChild != 0) 760 | current = current->rightChild; 761 | return current->x + nodeRadius * 3; 762 | } 763 | 764 | template 765 | void BinarySearchTree::recursiveDraw(Node *node) 766 | { 767 | // Base case 768 | if (node == 0) 769 | return; 770 | 771 | // Draw left subtree 772 | this->recursiveDraw(node->leftChild); 773 | 774 | // Set the y position of the node based off of the level of the node and the nodeRadius 775 | int level = getNodeLevel(node); 776 | int y = level * nodeRadius * 2 + yspace * (level-1); 777 | 778 | // if there is a left child, we need to draw this parent relative to it 779 | if (node->leftChild != 0) 780 | { 781 | node->x = getPxLocOfLeftTree(node->leftChild) + nodeRadius + xspace; 782 | 783 | // Draw line to left child 784 | painter->drawLine(QPoint(node->x, y + nodeRadius), QPoint(node->leftChild->x + 2,((level + 1)* nodeRadius * 2 + yspace * level) - nodeRadius)); 785 | } 786 | 787 | // in case of a node without a left child that is not the leftmost in the tree 788 | // - rules out root of tree (would be leftmost) 789 | // - must be the right child of some ancestor (parent, grandparent, etc..) 790 | // - must draw relative to first ancestor where x != 0 791 | else if (node->x == 0) 792 | node->x = getPxLocOfAncestor(node) + nodeRadius + xspace; 793 | 794 | // Draw the node 795 | painter->drawEllipse(QPoint(node->x, y),nodeRadius,nodeRadius); 796 | 797 | // Adjust the text horizontally depending on how many digits are in it 798 | int textAdjuster; 799 | if(std::abs(node->data) < 10) 800 | textAdjuster = 4; 801 | else if (std::abs(node->data) < 100) 802 | textAdjuster = 7; 803 | else if (std::abs(node->data) < 1000) 804 | textAdjuster = 12; 805 | else 806 | textAdjuster = 16; 807 | 808 | painter->drawText(QPoint(node->x-(textAdjuster*scale), y+(5*scale)), QString::number(node->data)); 809 | 810 | // Draw the right subtree 811 | this->recursiveDraw(node->rightChild); 812 | 813 | // Draw the line to the right child (if applicable). 814 | // Must be done after recursively drawing right child, otherwise x values will still be 0. 815 | if (node->rightChild != 0) 816 | painter->drawLine(QPoint(node->x, y + nodeRadius), QPoint(node->rightChild->x - 2,((level + 1)* nodeRadius * 2 + yspace * level) - nodeRadius)); 817 | 818 | return; 819 | } 820 | 821 | #endif /* BINARYSEARCHTREE_H_ */ 822 | --------------------------------------------------------------------------------