答案 0 :(得分:4)
您需要决定您的代码是否需要可移植。如果您可能需要使用GCC以外的编译器,嵌套函数对您的可移植性目标是致命的。我不会使用它们 - 但我的可移植性目标可能与你的不同。
您的代码遗失<wchar.h>
;如果没有它,它会相当干净地编译 - GCC抱怨缺少非静态函数的原型以及swprintf()
和fputwc()
),但添加<wchar.h>
会产生很多与{{swprintf()
相关的严重警告。 1}};他们实际上是在诊断一个错误。
gcc -O -I/Users/jleffler/inc -std=c99 -Wall -Wextra -Wmissing-prototypes \
-Wstrict-prototypes -Wold-style-definition -c tree.c
tree.c:88:6: warning: no previous prototype for ‘prettyprint_tree’
tree.c: In function ‘prettyprint_tree’:
tree.c:143:10: warning: no previous prototype for ‘recur_swprintf’
tree.c: In function ‘recur_swprintf’:
tree.c:156:17: warning: passing argument 2 of ‘swprintf’ makes integer from pointer without a cast
/usr/include/wchar.h:135:5: note: expected ‘size_t’ but argument is of type ‘int *’
tree.c:156:17: error: too few arguments to function ‘swprintf’
/usr/include/wchar.h:135:5: note: declared here
tree.c:160:13: warning: passing argument 2 of ‘swprintf’ makes integer from pointer without a cast
/usr/include/wchar.h:135:5: note: expected ‘size_t’ but argument is of type ‘int *’
tree.c:174:22: warning: passing argument 2 of ‘swprintf’ makes integer from pointer without a cast
/usr/include/wchar.h:135:5: note: expected ‘size_t’ but argument is of type ‘int *’
tree.c:174:22: warning: passing argument 3 of ‘swprintf’ makes pointer from integer without a cast
/usr/include/wchar.h:135:5: note: expected ‘const wchar_t * restrict’ but argument is of type ‘int’
tree.c:177:13: warning: passing argument 2 of ‘swprintf’ makes integer from pointer without a cast
/usr/include/wchar.h:135:5: note: expected ‘size_t’ but argument is of type ‘int *’
tree.c:177:13: error: too few arguments to function ‘swprintf’
/usr/include/wchar.h:135:5: note: declared here
tree.c: In function ‘prettyprint_tree’:
tree.c:181:10: warning: no previous prototype for ‘call_recur’
tree.c:188:9: warning: no previous prototype for ‘omit_cols’
(这是MacOS X 10.6.5上的GCC 4.5.2。)
swprintf()
的界面;它更像snprintf()
而不是sprintf()
(这是一件好事!)。整体想法很有趣。我建议在提交您的代码进行分析时选择一种表示形式,并清理与代码分析无关的任何内容。例如,arraystr
类型已定义但未使用 - 您不希望让像我这样的人在您的代码中获得便宜的镜头。与未使用的结构成员类似;甚至不要将它们留作注释,即使您可能希望将它们保留在VCS的代码中(尽管为什么?)。您使用的是版本控制系统(VCS),不是吗?这是一个修辞问题 - 如果你不使用VCS,现在就开始使用VCS,然后再失去你重视的东西。
在设计方面,你想避免做一些事情,比如要求主程序运行一个模糊的system()
命令 - 你的代码应该处理这些问题(可能有一个初始化函数,也许是一个终结函数撤消对终端设置所做的更改。)
不喜欢嵌套函数的另一个原因:我无法弄清楚如何获得函数的声明。似乎看似合理的替代方案不起作用 - 但我没有去阅读关于它们的GCC手册。
Minor nit:您可以告诉那些不使用'vi'或'vim'进行编辑的人 - 他们不会在第1列中放置函数的左括号。在'vi'中,第1列中的左括号从内部的任何地方为您提供一个简单的函数启动方式('[['向后跳跃;']]'跳转到下一个函数的开头。)
不要禁用断言。
包含主程序和相关测试数据 - 这意味着人们可以测试您的代码,而不仅仅是编译它。
使用宽字符常量而不是强制转换:
wcharbuf[i][j] = (wchar_t)' ';
wcharbuf[i][j] = L' ';
您的代码会创建一个大屏幕图像(代码中包含20行x 800列)并填写要打印的数据。这是一种合理的方式。小心,您可以安排处理线条绘制字符。但是,我认为您需要重新考虑核心绘图算法。您可能希望封装整个绘图代码,以便屏幕图像和相关信息在单个结构中,可以通过引用(指针)传递给函数。你有一组函数可以在树搜索代码指定的位置绘制各种位。您可以使用函数在适当的位置绘制数据值;你可以在适当的位置绘制线条。你可能没有嵌套函数 - 在我看来,当一个函数嵌套在另一个函数中时,它更难以读取代码。使功能静止是好的;使嵌套函数成为静态(非嵌套)函数。给他们他们需要的上下文 - 因此封装屏幕图像。
要求提供有关封装的信息......
您可以使用以下结构:
typedef struct columninfo Colinfo;
typedef struct Image
{
wchar_t image[WCHARBUF_LINES][WCHARBUF_COLUMNS];
Colinfo eachline[WCHARBUF_LINES];
} Image;
Image image;
您可能会发现添加一些额外成员是方便和/或明智的;这将在实施过程中显示出来。然后,您可以创建一个函数:
void format_node(Image *image, int line, int column, DTYPE value)
{
...
}
您还可以制作一些常量,例如将空格转换为枚举值:
enum { spacesafter = 2 };
然后,任何功能都可以使用它们。
答案 1 :(得分:2)
编码样式:prettyprint_tree()
函数需要过多的计算和数据,以便于阅读。例如,图像缓冲器的初始化和打印可以放置在单独的函数中,并且width
计算也可以。我相信您可以使用log
编写一个公式来替换
width = (max < 10) ? 1 :
(max < 100) ? 2 :
(max < 1000) ? 3 :
...
计算。
我不习惯读取嵌套函数和C,这使我更难扫描你的代码。除非您不与他人共享您的代码或将代码绑定到GCC的意识形态原因,否则我不会使用这些扩展。
算法:对于用C编写的快速而肮脏的漂亮打印机,我永远不会使用你的布局风格。与您的算法相比,编写有序遍历来打印
是明智的选择 a
/ \
b c
作为
c
a
b
我不介意不得不倾斜我的头。对于比我更漂亮的东西,我宁愿发射
digraph g { a -> b; a -> c; }
并将其留给dot进行格式化。
答案 2 :(得分:2)
此代码应该来自:http://www.ihas1337code.com/2010/09/how-to-pretty-print-binary-tree.html
#include <fstream>
#include <iostream>
#include <deque>
#include <iomanip>
#include <sstream>
#include <string>
#include <cmath>
using namespace std;
struct BinaryTree {
BinaryTree *left, *right;
int data;
BinaryTree(int val) : left(NULL), right(NULL), data(val) { }
};
// Find the maximum height of the binary tree
int maxHeight(BinaryTree *p) {
if (!p) return 0;
int leftHeight = maxHeight(p->left);
int rightHeight = maxHeight(p->right);
return (leftHeight > rightHeight) ? leftHeight + 1: rightHeight + 1;
}
// Convert an integer value to string
string intToString(int val) {
ostringstream ss;
ss << val;
return ss.str();
}
// Print the arm branches (eg, / \ ) on a line
void printBranches(int branchLen, int nodeSpaceLen, int startLen, int nodesInThisLevel, const deque<BinaryTree*>& nodesQueue, ostream& out) {
deque<BinaryTree*>::const_iterator iter = nodesQueue.begin();
for (int i = 0; i < nodesInThisLevel / 2; i++) {
out << ((i == 0) ? setw(startLen-1) : setw(nodeSpaceLen-2)) << "" << ((*iter++) ? "/" : " ");
out << setw(2*branchLen+2) << "" << ((*iter++) ? "\\" : " ");
}
out << endl;
}
// Print the branches and node (eg, ___10___ )
void printNodes(int branchLen, int nodeSpaceLen, int startLen, int nodesInThisLevel, const deque<BinaryTree*>& nodesQueue, ostream& out) {
deque<BinaryTree*>::const_iterator iter = nodesQueue.begin();
for (int i = 0; i < nodesInThisLevel; i++, iter++) {
out << ((i == 0) ? setw(startLen) : setw(nodeSpaceLen)) << "" << ((*iter && (*iter)->left) ? setfill('_') : setfill(' '));
out << setw(branchLen+2) << ((*iter) ? intToString((*iter)->data) : "");
out << ((*iter && (*iter)->right) ? setfill('_') : setfill(' ')) << setw(branchLen) << "" << setfill(' ');
}
out << endl;
}
// Print the leaves only (just for the bottom row)
void printLeaves(int indentSpace, int level, int nodesInThisLevel, const deque<BinaryTree*>& nodesQueue, ostream& out) {
deque<BinaryTree*>::const_iterator iter = nodesQueue.begin();
for (int i = 0; i < nodesInThisLevel; i++, iter++) {
out << ((i == 0) ? setw(indentSpace+2) : setw(2*level+2)) << ((*iter) ? intToString((*iter)->data) : "");
}
out << endl;
}
// Pretty formatting of a binary tree to the output stream
// @ param
// level Control how wide you want the tree to sparse (eg, level 1 has the minimum space between nodes, while level 2 has a larger space between nodes)
// indentSpace Change this to add some indent space to the left (eg, indentSpace of 0 means the lowest level of the left node will stick to the left margin)
void printPretty(BinaryTree *root, int level, int indentSpace, ostream& out) {
int h = maxHeight(root);
int nodesInThisLevel = 1;
int branchLen = 2*((int)pow(2.0,h)-1) - (3-level)*(int)pow(2.0,h-1); // eq of the length of branch for each node of each level
int nodeSpaceLen = 2 + (level+1)*(int)pow(2.0,h); // distance between left neighbor node's right arm and right neighbor node's left arm
int startLen = branchLen + (3-level) + indentSpace; // starting space to the first node to print of each level (for the left most node of each level only)
deque<BinaryTree*> nodesQueue;
nodesQueue.push_back(root);
for (int r = 1; r < h; r++) {
printBranches(branchLen, nodeSpaceLen, startLen, nodesInThisLevel, nodesQueue, out);
branchLen = branchLen/2 - 1;
nodeSpaceLen = nodeSpaceLen/2 + 1;
startLen = branchLen + (3-level) + indentSpace;
printNodes(branchLen, nodeSpaceLen, startLen, nodesInThisLevel, nodesQueue, out);
for (int i = 0; i < nodesInThisLevel; i++) {
BinaryTree *currNode = nodesQueue.front();
nodesQueue.pop_front();
if (currNode) {
nodesQueue.push_back(currNode->left);
nodesQueue.push_back(currNode->right);
} else {
nodesQueue.push_back(NULL);
nodesQueue.push_back(NULL);
}
}
nodesInThisLevel *= 2;
}
printBranches(branchLen, nodeSpaceLen, startLen, nodesInThisLevel, nodesQueue, out);
printLeaves(indentSpace, level, nodesInThisLevel, nodesQueue, out);
}
int main() {
BinaryTree *root = new BinaryTree(30);
root->left = new BinaryTree(20);
root->right = new BinaryTree(40);
root->left->left = new BinaryTree(10);
root->left->right = new BinaryTree(25);
root->right->left = new BinaryTree(35);
root->right->right = new BinaryTree(50);
root->left->left->left = new BinaryTree(5);
root->left->left->right = new BinaryTree(15);
root->left->right->right = new BinaryTree(28);
root->right->right->left = new BinaryTree(41);
cout << "Tree pretty print with level=1 and indentSpace=0\n\n";
// Output to console
printPretty(root, 1, 0, cout);
cout << "\n\nTree pretty print with level=5 and indentSpace=3,\noutput to file \"tree_pretty.txt\".\n\n";
// Create a file and output to that file
ofstream fout("tree_pretty.txt");
// Now print a tree that's more spread out to the file
printPretty(root, 5, 0, fout);
return 0;
}
答案 3 :(得分:0)
也许你可以看一下它可能适合你的Bresenham's line algorithm
答案 4 :(得分:0)
这是[{3}}提到的“快速而肮脏”方法的C实现。它不会变得更快和/或更脏:
void shittyprint_tree(tree *T){ // Supposed to be quick'n'dirty!
// When DTYPE is "char", width is a bit larger than needed.
if (T == NULL)
return;
const int width = ceil(log10(get_largest(T->root)+0.01)) + 2;
const wchar_t* sp64 = L" ";
void nested(node *ST, int spaces){ // GCC extension
if (ST == NULL){
wprintf(L"\n"); // Can be commented to disable the extra blanc line.
return;
}
nested(ST->right, spaces + width);
wprintf(L"%*.*s("DTYPE_PRINTF")\n", 0, spaces, sp64, 1, 1, ST->data);
nested(ST->left, spaces + width);
}
nested(T->root, 2);
}
示例输出(使用与以前相同的树):
(115.0) (113.0) (109.0) (107.0) (106.1) (106.0) (102.0) (101.5)
但我不能说它符合我原来的要求......