gpt4 book ai didi

c++ - 使用C++以漂亮的方式打印二叉树

转载 作者:行者123 更新时间:2023-12-02 10:26:27 25 4
gpt4 key购买 nike

我在尝试在c++中打印如下的二叉树时有点“迷”:

            8
/ \
/ \
/ \
5 10
/ \ / \
2 6 9 11

我知道如何获取树的高度和每个级别中的节点数,但是我无法弄清楚如何在根和第二级之间设置正确的空格数(根下有3行3个级别,但我相信并非每次都这样,我认为这可能是大树的高度的3倍)。

我想在打印行中的这些空格以及行之间的行数方面有所帮助。谢谢。

我在用C++编码
Get height

int tree::getHeight(No *node) {
if (node == NULL) return 0;
return 1 + max(getHeight(node->esq), getHeight(node->dir));
}

Get number of nodes per line

void tree::getLine(const No *root, int depth, vector<int>& vals){
int placeholder = 10;
if (depth <= 0 && root != nullptr) {
vals.push_back(root->chave);
return;
}
if (root->esq != nullptr)
getLine(root->esq, depth-1, vals);
else if (depth-1 <= 0)
vals.push_back(placeholder);
if (root->dir != nullptr)
getLine(root->dir, depth-1, vals);
else if (depth-1 <= 0)
vals.push_back(placeholder);
}

最佳答案

这是创建二进制树的基于文本的表示形式的代码示例。本演示使用了最小有用的二叉树类(BinTree),并且具有很小的占用空间,只是为了避免膨胀示例的大小。

它的文本呈现成员函数更严重,使用迭代而不是递归,如该类其他部分所述。

这分三步完成,首先将一个由字符串值组成的行 vector 放在一起。

然后,这用于格式化代表树的文本字符串行。

然后,将字符串清理干净并转交给cout。

此外,该演示还包括“随机树”功能,可连续播放数小时。

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
#include <random>

using std::vector;
using std::string;
using std::cout;

template <typename T>
class BinTree {
struct Node {
T value;
Node *left,*right;
Node() : left(nullptr),right(nullptr) {}
Node(const T& value) :value(value),left(nullptr),right(nullptr) {}
// stack-abusing recursion everywhere, for small code
~Node() { delete left; delete right; }
int max_depth() const {
const int left_depth = left ? left->max_depth() : 0;
const int right_depth = right ? right->max_depth() : 0;
return (left_depth > right_depth ? left_depth : right_depth) + 1;
}
};

Node *root;

public:
BinTree() : root(nullptr) {}
~BinTree() { delete root; }

int get_max_depth() const { return root ? root->max_depth() : 0; }
void clear() { delete root; root = nullptr; }
void insert() {}
template <typename ...Args>
void insert(const T& value, Args...more) {
if(!root) {
root = new Node(value);
} else {
Node* p = root;
for(;;) {
if(value == p->value) return;
Node* &pchild = value < p->value ? p->left : p->right;
if(!pchild) {
pchild = new Node(value);
break;
}
p = pchild;
}
}
insert(more...);
}

struct cell_display {
string valstr;
bool present;
cell_display() : present(false) {}
cell_display(std::string valstr) : valstr(valstr), present(true) {}
};

using display_rows = vector< vector< cell_display > >;

// The text tree generation code below is all iterative, to avoid stack faults.

// get_row_display builds a vector of vectors of cell_display structs
// each vector of cell_display structs represents one row, starting at the root
display_rows get_row_display() const {
// start off by traversing the tree to
// build a vector of vectors of Node pointers
vector<Node*> traversal_stack;
vector< std::vector<Node*> > rows;
if(!root) return display_rows();

Node *p = root;
const int max_depth = root->max_depth();
rows.resize(max_depth);
int depth = 0;
for(;;) {
// Max-depth Nodes are always a leaf or null
// This special case blocks deeper traversal
if(depth == max_depth-1) {
rows[depth].push_back(p);
if(depth == 0) break;
--depth;
continue;
}

// First visit to node? Go to left child.
if(traversal_stack.size() == depth) {
rows[depth].push_back(p);
traversal_stack.push_back(p);
if(p) p = p->left;
++depth;
continue;
}

// Odd child count? Go to right child.
if(rows[depth+1].size() % 2) {
p = traversal_stack.back();
if(p) p = p->right;
++depth;
continue;
}

// Time to leave if we get here

// Exit loop if this is the root
if(depth == 0) break;

traversal_stack.pop_back();
p = traversal_stack.back();
--depth;
}

// Use rows of Node pointers to populate rows of cell_display structs.
// All possible slots in the tree get a cell_display struct,
// so if there is no actual Node at a struct's location,
// its boolean "present" field is set to false.
// The struct also contains a string representation of
// its Node's value, created using a std::stringstream object.
display_rows rows_disp;
std::stringstream ss;
for(const auto& row : rows) {
rows_disp.emplace_back();
for(Node* pn : row) {
if(pn) {
ss << pn->value;
rows_disp.back().push_back(cell_display(ss.str()));
ss = std::stringstream();
} else {
rows_disp.back().push_back(cell_display());
} } }
return rows_disp;
}

// row_formatter takes the vector of rows of cell_display structs
// generated by get_row_display and formats it into a test representation
// as a vector of strings
vector<string> row_formatter(const display_rows& rows_disp) const {
using s_t = string::size_type;

// First find the maximum value string length and put it in cell_width
s_t cell_width = 0;
for(const auto& row_disp : rows_disp) {
for(const auto& cd : row_disp) {
if(cd.present && cd.valstr.length() > cell_width) {
cell_width = cd.valstr.length();
} } }

// make sure the cell_width is an odd number
if(cell_width % 2 == 0) ++cell_width;

// formatted_rows will hold the results
vector<string> formatted_rows;

// some of these counting variables are related,
// so its should be possible to eliminate some of them.
s_t row_count = rows_disp.size();

// this row's element count, a power of two
s_t row_elem_count = 1 << (row_count-1);

// left_pad holds the number of space charactes at the beginning of the bottom row
s_t left_pad = 0;

// Work from the level of maximum depth, up to the root
// ("formatted_rows" will need to be reversed when done)
for(s_t r=0; r<row_count; ++r) {
const auto& cd_row = rows_disp[row_count-r-1]; // r reverse-indexes the row
// "space" will be the number of rows of slashes needed to get
// from this row to the next. It is also used to determine other
// text offsets.
s_t space = (s_t(1) << r) * (cell_width + 1) / 2 - 1;
// "row" holds the line of text currently being assembled
string row;
// iterate over each element in this row
for(s_t c=0; c<row_elem_count; ++c) {
// add padding, more when this is not the leftmost element
row += string(c ? left_pad*2+1 : left_pad, ' ');
if(cd_row[c].present) {
// This position corresponds to an existing Node
const string& valstr = cd_row[c].valstr;
// Try to pad the left and right sides of the value string
// with the same number of spaces. If padding requires an
// odd number of spaces, right-sided children get the longer
// padding on the right side, while left-sided children
// get it on the left side.
s_t long_padding = cell_width - valstr.length();
s_t short_padding = long_padding / 2;
long_padding -= short_padding;
row += string(c%2 ? short_padding : long_padding, ' ');
row += valstr;
row += string(c%2 ? long_padding : short_padding, ' ');
} else {
// This position is empty, Nodeless...
row += string(cell_width, ' ');
}
}
// A row of spaced-apart value strings is ready, add it to the result vector
formatted_rows.push_back(row);

// The root has been added, so this loop is finsished
if(row_elem_count == 1) break;

// Add rows of forward- and back- slash characters, spaced apart
// to "connect" two rows' Node value strings.
// The "space" variable counts the number of rows needed here.
s_t left_space = space + 1;
s_t right_space = space - 1;
for(s_t sr=0; sr<space; ++sr) {
string row;
for(s_t c=0; c<row_elem_count; ++c) {
if(c % 2 == 0) {
row += string(c ? left_space*2 + 1 : left_space, ' ');
row += cd_row[c].present ? '/' : ' ';
row += string(right_space + 1, ' ');
} else {
row += string(right_space, ' ');
row += cd_row[c].present ? '\\' : ' ';
}
}
formatted_rows.push_back(row);
++left_space;
--right_space;
}
left_pad += space + 1;
row_elem_count /= 2;
}

// Reverse the result, placing the root node at the beginning (top)
std::reverse(formatted_rows.begin(), formatted_rows.end());

return formatted_rows;
}

// Trims an equal number of space characters from
// the beginning of each string in the vector.
// At least one string in the vector will end up beginning
// with no space characters.
static void trim_rows_left(vector<string>& rows) {
if(!rows.size()) return;
auto min_space = rows.front().length();
for(const auto& row : rows) {
auto i = row.find_first_not_of(' ');
if(i==string::npos) i = row.length();
if(i == 0) return;
if(i < min_space) min_space = i;
}
for(auto& row : rows) {
row.erase(0, min_space);
} }

// Dumps a representation of the tree to cout
void Dump() const {
const int d = get_max_depth();

// If this tree is empty, tell someone
if(d == 0) {
cout << " <empty tree>\n";
return;
}

// This tree is not empty, so get a list of node values...
const auto rows_disp = get_row_display();
// then format these into a text representation...
auto formatted_rows = row_formatter(rows_disp);
// then trim excess space characters from the left sides of the text...
trim_rows_left(formatted_rows);
// then dump the text to cout.
for(const auto& row : formatted_rows) {
std::cout << ' ' << row << '\n';
}
}
};


int main() {
BinTree<int> bt;

// Build OP's tree
bt.insert(8,5,2,6,10,9,11);
cout << "Tree from OP:\n\n";
bt.Dump();
cout << "\n\n";

bt.clear();

// Build a random tree
// This toy tree can't balance, so random
// trees often look more like linked lists.
// Just keep trying until a nice one shows up.
std::random_device rd;
std::mt19937 rng(rd());

int MaxCount=20;
int MaxDepth=5;
const int Min=0, Max=1000;

std::uniform_int_distribution<int> dist(Min,Max);

while(MaxCount--) {
bt.insert(dist(rng));
if(bt.get_max_depth() >= MaxDepth) break;
}

cout << "Randomly generated tree:\n\n";
bt.Dump();
}

输出示例:

Tree from OP:

8
/ \
/ \
/ \
5 10
/ \ / \
2 6 9 11


Randomly generated tree:

703
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
/ \
137 965
/ \ /
/ \ /
/ \ /
/ \ /
/ \ /
/ \ /
/ \ /
41 387 786
\ / \ / \
\ / \ / \
\ / \ / \
95 382 630 726 813
\
841

关于c++ - 使用C++以漂亮的方式打印二叉树,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64310533/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com