patterncppMinor
Binary search tree
Viewed 0 times
binarytreesearch
Problem
This is a binary tree using classes. What can be improved upon?
ClassTree.h
ForClass.cpp
```
#include "ClassTree.h"
#include
int Node::count = 0;
Node::Node(){
count++;
}
int Node::get_count(){
return count;
}
void Node::zero_count(){
count = 0;
}
Node Node::first(Node root){
if(count){
cout > temp;
root = new Node;
root->id = temp;
root->left = 0;
root->right = 0;
}
return root;
}
void Node::search(Node *root, int digit){
Node *pv = root;
bool found = false;
while (pv && !found){
if (digit == pv->id){
cout id){
pv = pv -> left;
}
else{
pv = pv -> right;
}
}
cout id){
return pv;
}
else if (digit id){
pv = pv -> left;
}
else{
pv = pv -> right;
}
}
Node *pnew = new Node;
pnew->id = digit;
pnew->left = 0;
pnew->right = 0;
if (digit id){
prev->left = pnew;
}
else{
prev->right = pnew;
}
return pnew;
}
void Node::print_tree(Node *root, int level){
if( !get_count() ){
cout left, level + 1);
for (int i = 0; i id right, level +1);
}
}
void Node::max_internal(Node *root){
if( !get_count() ){
cout left == 0 && root->right == 0){
cout id id;
Node *pv = root;
while (pv){
temp_max = pv->id;
pv = pv -> right;
if(pv->left == 0 && pv->right == 0){
ClassTree.h
#pragma once
class Node{
private:
int id;
Node *left;
Node *right;
static int count;
public:
Node();
Node * first(Node *root);
void search(Node *root, int digit);
Node * insert(Node *root, int digit);
void print_tree(Node *root, int level);
void max_internal(Node *root);
Node * delete_key(Node *root, int digit);
void delete_tree(Node * root);
static int get_count();
static void zero_count();
};ForClass.cpp
```
#include "ClassTree.h"
#include
int Node::count = 0;
Node::Node(){
count++;
}
int Node::get_count(){
return count;
}
void Node::zero_count(){
count = 0;
}
Node Node::first(Node root){
if(count){
cout > temp;
root = new Node;
root->id = temp;
root->left = 0;
root->right = 0;
}
return root;
}
void Node::search(Node *root, int digit){
Node *pv = root;
bool found = false;
while (pv && !found){
if (digit == pv->id){
cout id){
pv = pv -> left;
}
else{
pv = pv -> right;
}
}
cout id){
return pv;
}
else if (digit id){
pv = pv -> left;
}
else{
pv = pv -> right;
}
}
Node *pnew = new Node;
pnew->id = digit;
pnew->left = 0;
pnew->right = 0;
if (digit id){
prev->left = pnew;
}
else{
prev->right = pnew;
}
return pnew;
}
void Node::print_tree(Node *root, int level){
if( !get_count() ){
cout left, level + 1);
for (int i = 0; i id right, level +1);
}
}
void Node::max_internal(Node *root){
if( !get_count() ){
cout left == 0 && root->right == 0){
cout id id;
Node *pv = root;
while (pv){
temp_max = pv->id;
pv = pv -> right;
if(pv->left == 0 && pv->right == 0){
Solution
OK, let's start at the first question you should ask yourself when writing code:
Am I helping the reader figure out what's going on?
Let's start by looking at
So this brings me to my first concrete suggestion: Never declare a function that's defined in an unrelated file. Instead, declare them in a header file with the same basename as the source file that defines them.
Second concrete suggestion: Give things meaningful names. What does this program do? I don't know, the source file is named
Third concrete suggestion: Document your classes and functions! Documentation starts with a good name, but when some behavior isn't obvious from the name, it should be present in a comment.
Now let's visit
As a relatively minor point, let me say that you should always put your code in namespaces. When you put everything into the global namespace, you are significantly increasing the likelihood of name collisions, which can sometimes not be detected by the compiler or linker and which may lead to subtly wrong behavior at run time.
Now let's visit your class design. Others have mentioned some of the problems here, but most of them come from one error at the root: You have inappropriately overspecialized the design of this class to this specific use case. You've written a little test program here to exercise your binary tree, but you've basically made the tree unusable in other programs. Let's look at some of the problems that arise from that:
There are a few other problems as well, which I'll call out as I lay out a possible implementation of
Quick note -- include the C++ headers, not the C-ish headers like
Normally I give the public API before private implementation details, but the
Note: In C++14, these should be
A couple notes on
- There's no need to pass the root node as its first argument;
- Passing raw poin
Am I helping the reader figure out what's going on?
Let's start by looking at
Main.cpp. Here, there are a few small clues that might help me figure out what's going on, but they're misleading. First, you are including a file called ClassTree.h. This name suggests to me that it provides a class called ClassTree, or at the very least a module that is concerned with class trees. What's a class tree? Well, I've never heard of it before, but the name suggest that it's a tree of classes. Maybe it's for something in biology, but it seems more like it's for some sort of code analysis. Next we have main, which has no comments, and only references the names P1, Node, and menu, which are very close to the least informative names you could have in a program. Well, let's take a look at what menu does, maybe we can get a hint. Wait, there's no definition for menu, only a declaration. We don't even have a clue as to where it's defined; I guess we'll have to look at a Makefile or grep for menu in the other files in this directory.So this brings me to my first concrete suggestion: Never declare a function that's defined in an unrelated file. Instead, declare them in a header file with the same basename as the source file that defines them.
Second concrete suggestion: Give things meaningful names. What does this program do? I don't know, the source file is named
Main.cpp and the library that defines its core functionality is called ForMain.cpp.Third concrete suggestion: Document your classes and functions! Documentation starts with a good name, but when some behavior isn't obvious from the name, it should be present in a comment.
Now let's visit
ClassTree.h. As alluded to earlier, this isn't a very good name for this file. It's not a tree of classes. The data structure you're defining is a binary tree. The fact that it's implemented using a class is (a) in some sense an implementation detail, and therefore not the thing to emphasize in its name, and (b) essentially assumed in C++. So let's call it BinaryTree.h.As a relatively minor point, let me say that you should always put your code in namespaces. When you put everything into the global namespace, you are significantly increasing the likelihood of name collisions, which can sometimes not be detected by the compiler or linker and which may lead to subtly wrong behavior at run time.
Now let's visit your class design. Others have mentioned some of the problems here, but most of them come from one error at the root: You have inappropriately overspecialized the design of this class to this specific use case. You've written a little test program here to exercise your binary tree, but you've basically made the tree unusable in other programs. Let's look at some of the problems that arise from that:
- The
countmechanism makes the assumption that you will only ever have one tree in your program. If you have multiple trees, they will trample each other's counts.
- You have committed yourself to
intdata in the nodes. But binary trees like this work with lots of data types, as long as they can be ordered and compared for equality. This should be a template class!
Node's methods have confusing names and signatures, and no documentation. This probably wasn't obvious to you since you only wrote a short program usingNodearound the same time as you wroteNode.
There are a few other problems as well, which I'll call out as I lay out a possible implementation of
BinaryTree.h (I've written this in C++11, which is now widely available; I've avoided some improvements that could be made using C++14):#pragma once
#include
#include
#include Quick note -- include the C++ headers, not the C-ish headers like
iostream.h — they're ancient.#include
#include
namespace dimanist_binary_tree {Normally I give the public API before private implementation details, but the
Node class illustrates many of the points I want to make, so here it is.namespace detail {
template ,
typename Eq = std::equal_to>Note: In C++14, these should be
std::less<> and std::equal_to<>, which are more flexible than std::less and std::equal_to.class Node : std::enabled_shared_from_this {
public:
using LessType = Less;
using EqType = Eq;
Node(T&& t) : data(std::move(t)) {}
// In-place construction of data
template
Node(Args&&... args) : data(std::forward(args)...) {}
// Inserts a new node into this tree with t as its data.
void insert(T&& t, Less less) {
return insert(std::make_shared(std::move(t)), less);
}
// Inserts node into this tree.
void insert(std::shared_ptr node, Less less);A couple notes on
insert:- There's no need to pass the root node as its first argument;
this is already the root node of a subtree. This is C++, not C!- Passing raw poin
Code Snippets
#pragma once
#include <cassert>
#include <functional>
#include <iostream>#include <memory>
#include <utility>
namespace dimanist_binary_tree {namespace detail {
template <typename T,
typename Less = std::less<T>,
typename Eq = std::equal_to<T>>class Node : std::enabled_shared_from_this<Node> {
public:
using LessType = Less;
using EqType = Eq;
Node(T&& t) : data(std::move(t)) {}
// In-place construction of data
template <typename... Args>
Node(Args&&... args) : data(std::forward<Args>(args)...) {}
// Inserts a new node into this tree with t as its data.
void insert(T&& t, Less less) {
return insert(std::make_shared<Node>(std::move(t)), less);
}
// Inserts node into this tree.
void insert(std::shared_ptr<Node> node, Less less);// If t is present in this tree, one node containing it is removed.
// The return value contains the new root of this tree and a bool
// that is true if any node was removed.
std::pair<std::shared_ptr<Node>, bool> erase(const T& t, Less less, Eq eq);
// If t is present in this tree, all nodes containing it are removed.
// The return value contains the new root of this tree and an int
// giving the number of nodes removed.
std::pair<std::shared_ptr<Node>, int> erase_all(const T& t, Less less, Eq eq);Context
StackExchange Code Review Q#67014, answer score: 8
Revisions (0)
No revisions yet.