先看效果:
主要算法流程:
0. 按层遍历二叉树并确定每一个节点的相对位置和每一层的最后一个节点(在每一个节点的数据结构中增加一项pos,代表当前节点的位置)。
1. 递归获取每一棵子树的最左位置与最右位置。
2. 根据1获取的位置进行冲突调整,主要存在以下两种冲突:(设当前节点为cur,当前节点左子树最右节点为lchild_most_r,右子树最左节点为rchild_most_l)
0):lchild_most_r->pos >= cur->pos
对于这种情况我的调整方法为cur及其rchild全部节点的位置向右移动lchild_most_r->pos - cur->pos + 1位置。
1):rchild_most_l->pos <= cur->pos
对于这种情况将rchild全部节点向右移动cur->pos - rchild_most_l->pos位置。
并在移动的过程中继续递归获取每一棵子树的最左位置与最右位置进行冲突调整,最后返回整棵树的最左位置min_pos。
3. 由于树的某些节点位置可能为负,因此将整棵树的位置向右移动min_pos位置。
4. 至此每一个节点的最终(相对)位置已经确定,已经可以较直观的打印出来树的形状了,不过我又做了一些添加,也就是将节点连接起来,具体细节不在赘述,详见代码。
#include <iostream> #include <string> #include <queue> using namespace std; #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define MAX(x, y) ((x) > (y) ? (x) : (y)) #define ALIGN(x, c); {for(int i = 0; i<x; ++i) cout << c;} int intlen(int _x) { int len = 0; if (_x == 0) return 1; while (_x != 0) { _x /= 10; ++len; } return len; } string int2str(int _x) { string tmp, res; do { tmp.push_back((_x % 10) + '0'); _x /= 10; } while (_x != 0); for (size_t index = tmp.size() - 1; index != -1; --index) res.push_back(tmp[index]); return res; } class binaryTree { private: struct node { int value; node *lchild; node *rchild; int pos; node(int _value = 0, int _pos = 0, node *_lchild = nullptr, node *_rchild = nullptr) : value(_value), lchild(_lchild), rchild(_rchild), pos(_pos) {} }; struct returnData { int l_pos; int r_pos; returnData(int _l_most_r = INT_MAX, int _r_most_l = INT_MIN) : l_pos(_l_most_r), r_pos(_r_most_l) {} }; node *src_root; node *res_root; private: void nodesPosAdj(node *_head, int _changes) { if (_head == nullptr) return; if (_head->lchild) _head->lchild->pos += _changes; if (_head->rchild) _head->rchild->pos += _changes; nodesPosAdj(_head->lchild, _changes); nodesPosAdj(_head->rchild, _changes); } returnData binaryPosAdj(node *_root, node *_head) { int changes = 0; returnData tmp, lchild_data, rchild_data; if (_head == nullptr) return tmp; lchild_data = binaryPosAdj(_root, _head->lchild); rchild_data = binaryPosAdj(_root, _head->rchild); while (_head->lchild != nullptr && lchild_data.r_pos >= _head->pos) { changes = lchild_data.r_pos - _head->pos + 2; _head->pos += changes; _head->rchild->pos += changes; nodesPosAdj(_head->rchild, changes); if (_head != _root) rchild_data = binaryPosAdj(_root, _head->rchild); } while (_head->rchild != nullptr && rchild_data.l_pos <= _head->pos) { changes = _head->pos - rchild_data.l_pos + 2; _head->rchild->pos += changes; nodesPosAdj(_head->rchild, changes); //if (_head != _root) rchild_data = binaryPosAdj(_root, _head->rchild); } tmp.l_pos = MIN(_head->pos, lchild_data.l_pos); tmp.r_pos = MAX(_head->pos, rchild_data.r_pos); return tmp; } void binaryTreePrint_bkp(node *_root) { #define FILLSPACE(src, nums, c); { for(size_t index = 0; index < nums; ++index) src.push_back(c); } queue<node *>src; node *tmp = nullptr; int level = 0; int pre_pos = 0, min_pos = 0, pre_len = 0; vector<int> nodes_per_level(2, 0); //level nodes; string chars; if (_root == nullptr) return; min_pos = binaryPosAdj(_root, _root).l_pos; min_pos = abs(min_pos); _root->pos += min_pos; src.push(_root); nodes_per_level[level] = 1; while (!src.empty()) { tmp = src.front(); src.pop(); ALIGN(tmp->pos - pre_pos - pre_len, ' '); pre_pos = tmp->pos; pre_len = intlen(tmp->value); cout << tmp->value; if (tmp->lchild) { tmp->lchild->pos += min_pos; src.push(tmp->lchild); nodes_per_level[level + 1]++; } if (tmp->rchild) { tmp->rchild->pos += min_pos; src.push(tmp->rchild); nodes_per_level[level + 1]++; } if (--nodes_per_level[level] == 0) { cout << endl; nodes_per_level.push_back(0); ++level; pre_pos = 0; pre_len = 0; } } } void refreshNodesPos(int _min_pos, node *_head) { if (_head == nullptr) return; _head->pos += _min_pos; if (_head->lchild) refreshNodesPos(_min_pos, _head->lchild); if (_head->rchild) refreshNodesPos(_min_pos, _head->rchild); } public: binaryTree() : src_root(nullptr), res_root(nullptr) {} void generateFixedBinaryTree(void) { queue<node *> src; node *tmp = nullptr; int x = 0, lchild = 0, rchild = 0; cout << "please enter root value: "; cin >> x; src.push(src_root = new node(x)); while (!src.empty()) { tmp = src.front(); src.pop(); cout << "please enter " << tmp->value << "'s lchild and rchild: "; cin >> lchild >> rchild; if (lchild != -1) src.push(tmp->lchild = new node(lchild)); if (rchild != -1) src.push(tmp->rchild = new node(rchild)); } } void generateFixedBinaryTree(vector<int> _nodes) { node *tmp = nullptr; queue<node *> x; int index = 0; int min_pos = 0; if (_nodes[0] == '#') return; x.push(src_root = new node(_nodes[index++], 0)); min_pos = src_root->pos; while (!x.empty()) { tmp = x.front(); x.pop(); if (_nodes[index] != '#') { x.push(tmp->lchild = new node(_nodes[index], (tmp->pos - intlen(_nodes[index]) - 1))); min_pos = MIN(min_pos, (tmp->pos - intlen(_nodes[index]) - 1)); } ++index; if (_nodes[index] != '#') x.push(tmp->rchild = new node(_nodes[index], (tmp->pos + (intlen(tmp->value) + 1)))); ++index; } } void generateRandomBinaryTree(int _maxsize, int _maxvalue) { node *tmp = nullptr; queue< node * > src; int nullvalue = (_maxvalue >> 1); int value = 0; int maxsize = (rand() % _maxsize); int size = 0; src_root = new node(rand() % _maxvalue); if (src_root->value == nullvalue) return; ++size; src.push(src_root); while ((!src.empty()) && (size < maxsize)) { tmp = src.front(); src.pop(); if (tmp->value != '#') { value = rand() % _maxvalue; if (value != nullvalue) {//((value > (nullvalue - 10)) && (value > (nullvalue + 10))) { ++size; src.push(tmp->lchild = new node(value)); } else src.push(tmp->lchild = new node('#')); value = rand() % _maxvalue; if (value != nullvalue) {//((value > (nullvalue - 10)) && (value > (nullvalue + 10))) { ++size; src.push(tmp->rchild = new node(value)); } else src.push(tmp->rchild = new node('#')); } } } void generateRandomBinaryTree(vector<int> _nodes) { int flag = 1; //0: lchild 1: rchild 2: lchild&&rchild queue<node *> x; node *tmp = nullptr; int index = 1; src_root = new node(_nodes[0]); x.push(src_root); while (index < _nodes.size() && !x.empty()) { tmp = x.front(); x.pop(); flag = (rand() % 3); //flag = 0 / 1 / 2 if (flag == 0) { tmp->lchild = new node(_nodes[index++]); x.push(tmp->lchild); } else if (flag == 1) { tmp->rchild = new node(_nodes[index++]); x.push(tmp->rchild); } else { tmp->lchild = new node(_nodes[index++]); tmp->rchild = new node(_nodes[index++]); x.push(tmp->lchild); x.push(tmp->rchild); } } } void binaryTreePrint(void) { queue<node *>src; node *tmp = nullptr; int level = 0; int pre_num_pos = 0, pre_line_pos = 0, min_pos = 0, pre_len = 0, fill_len = 0; vector<int> nodes_per_level(2, 0); //level nodes; vector<string> chars(1); vector<string> nums(1); if (src_root == nullptr) return; min_pos = binaryPosAdj(src_root, src_root).l_pos; refreshNodesPos(abs(min_pos), src_root); src.push(src_root); nodes_per_level[level] = 1; while (!src.empty()) { tmp = src.front(); src.pop(); fill_len = tmp->pos - pre_num_pos - pre_len; chars[level] += *new string((tmp->pos - pre_line_pos - pre_len), ' '); chars[level] += pre_len > 1 ? *new string(pre_len - 1, ' ') : ""; chars[level] += '|'; if (tmp->lchild) { nums[level] += *new string(tmp->lchild->pos - (tmp->pos - fill_len), ' '); nums[level] += *new string(tmp->pos - tmp->lchild->pos, '-'); } else nums[level] += *new string(fill_len, ' '); nums[level] += int2str(tmp->value); pre_num_pos = tmp->pos; pre_line_pos = tmp->pos; pre_len = intlen(tmp->value); if (tmp->rchild) { int len = intlen(tmp->rchild->value); len = tmp->rchild->pos - tmp->pos - (len > 1 ? len - 1 : 0); nums[level] += *new string(len, '-'); pre_num_pos += len; } if (tmp->lchild) { src.push(tmp->lchild); nodes_per_level[level + 1]++; } if (tmp->rchild) { src.push(tmp->rchild); nodes_per_level[level + 1]++; } if (--nodes_per_level[level] == 0) { nums[level] += '\n'; nums.push_back(""); chars[level] += '\n'; chars.push_back(""); nodes_per_level.push_back(0); ++level; pre_num_pos = 0; pre_line_pos = 0; pre_len = 0; } } int index_nums = 0, index_chars = 0; for (size_t index = 0; index < nums.size() - 1; ++index) { cout << nums[index_nums++]; cout << chars[++index_chars]; } } }; int main(void) { vector<int> src_nodes({ 6, 1, 12, 0, 3, 10, 13, '#', '#', '#', '#', 4, 14, 20, 16, 2, 5, 11, 15, '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#' }); vector<int> src_nodes2({ 2, 3, 4, 5, 6, 3, 5, '#', '#', '#', '#', 1, 2, 3, 4, '#', '#','#', '#', '#', '#','#', '#' }); binaryTree tree; tree.generateFixedBinaryTree(src_nodes); tree.binaryTreePrint(); cout << endl << endl; tree.generateFixedBinaryTree(src_nodes2); tree.binaryTreePrint(); system("pause"); return 0; }