C++ 使用c++;
在c++中打印二叉树时,我“有点”迷路了,如下所示:C++ 使用c++;,c++,binary-tree,C++,Binary Tree,在c++中打印二叉树时,我“有点”迷路了,如下所示: 8 / \ / \ / \ 5 10 / \ / \ 2 6 9 11 我知道如何获得树的高度和每个级别中的节点数,但我不知道如何在根和第二个级别之间设置正确的空间数(根下有3条线用于3个级别,但我相信不是每次都是这样,我认为对于更大的树,它可能是高度的3倍) 我想得到
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);
}
获取高度
int-tree::getHeight(无*节点){
如果(node==NULL)返回0;
返回1+max(getHeight(node->esq),getHeight(node->dir));
}
获取每行的节点数
void tree::getLine(常量编号*根、整数深度、向量和VAL){
int占位符=10;
if(深度转换);
返回;
}
if(root->esq!=nullptr)
getLine(root->esq,depth-1,vals);
else if(深度-1目录!=nullptr)
getLine(root->dir,depth-1,vals);
else if(depth-1这里是一个创建基于文本的二叉树表示的代码示例。此演示使用一个占用空间小的最小有用的二叉树类(BinTree),只是为了避免使示例的大小膨胀
它的文本呈现成员函数更为重要,使用迭代而不是递归,正如在类的其他部分中所发现的那样
这项工作分三个步骤完成,首先将字符串值行的向量放在一起
然后,这用于格式化表示树的文本字符串行
然后将琴弦清理干净并倾倒到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();
}
下面是一个创建基于文本的二叉树表示的代码示例。此演示使用一个占用空间小的最小实用二叉树类(BinTree),以避免使示例的大小膨胀
它的文本呈现成员函数更为重要,使用迭代而不是递归,正如在类的其他部分中所发现的那样
这项工作分三个步骤完成,首先将字符串值行的向量放在一起
然后,这用于格式化表示树的文本字符串行
然后将琴弦清理干净并倾倒到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();
}
- 第一个功能-逐级打印(根lv->左lv)
- 第二个功能-距新线起点的距离
- 第三个功能-打印节点并计算两次打印之间的距离
输出:
- 第一个功能-逐级打印(根lv->左lv)
- 第二个功能-距新线起点的距离
- 第三个功能-打印节点并计算两次打印之间的距离
输出:即使很难,这也不完全是您所要求的,水平打印树要简单得多。特别是对于大树,我认为这是更好的表示形式
└──8
├──5
│ ├──2
│ └──6
└──10
├──9
└──11
遵循C++代码根in ./p>
void printBT(const std::string和prefix,const BSTNode*node,bool isLeft)
{
如果(节点!=nullptr)
{
std::cout即使很难,这也不完全是您所要求的,水平打印树要简单得多。特别是对于大型树,我认为这是更好的表示形式
└──8
├──5
│ ├──2
│ └──6
└──10
├──9
└──11
遵循C++代码根in ./p>
void printBT(const std::string和prefix,const BSTNode*node,bool isLeft)
{
如果(节点!=nullptr)
{
STD::CUT< P>我写了任意树漂亮的打印机作为C++算法的一部分自我教育。
方法如下
- 从每个树节点可打印节点与字符串化原始节点值和绝对位置组成的行
- 同级可打印节点已分组。每个同级组包含节点列表和指向父级可打印节点的指针
- 同级组分组为行,每行表示原始树级别
接下来,计算可打印节点的位置
- 行通过跳过第一行进行迭代
- 行中的兄弟节点迭代后,每个兄弟节点组移动到其父节点中心(如果中心比组的中间更远)。如果与上一个兄弟节点组相交,则移动得更远。如果中间比父节点中心更远,则父节点移动到子节点的中间。如果与移位的父节点相交
- 对兄弟组父兄弟组递归重复上一步
对于最后一步,再次迭代以写入提供的输出流的行,根据计算的节点位置填充空格偏移量
Unix方框绘图符号用于绘制线条。不确定它们是否能在Windows cmd中正确打印,可能应该用Windows的DOS对应符号替换它们
1
┌────────────┬────────┴────────────────────┐
11 12 13
┌────┼────┐ ┌──┴─┐ ┌─────────┴────┬────┐
111 112 113 121 122 131 132 133
┌─────┼─────┐ ┌─────┼─────┐ ┌──┴──┐
1221 1222 1223 1311 1312 1313 1321 1322
< /P> < P>我编写了任意树漂亮的打印机作为C++算法的一部分自教育。
方法如下
- 从每个树节点可打印节点与字符串化原始节点值和绝对位置组成的行
- 同级可打印节点已分组。每个同级组包含节点列表和指向父级可打印节点的指针
- 同级组分组为行,每行表示原始树级别
接下来,计算可打印节点的位置
- 行通过跳过第一行进行迭代
- 行中的兄弟节点迭代后,每个兄弟节点组移动到其父节点中心(如果中心比组的中间更远)。如果与前一个兄弟节点组相交,则移动得更远。如果中间比前一个兄弟节点组更远,则父节点移动到子节点的中间
1
┌────────────┬────────┴────────────────────┐
11 12 13
┌────┼────┐ ┌──┴─┐ ┌─────────┴────┬────┐
111 112 113 121 122 131 132 133
┌─────┼─────┐ ┌─────┼─────┐ ┌──┴──┐
1221 1222 1223 1311 1312 1313 1321 1322