二叉树显示(图形界面,控制台字符),简单表达式求值,pyqt,swig初试....
最近复习数据结构,练习了一些二叉树算法,其中如果能够将二叉树直观显示出来能够很好的检测算法的正确性。
写了控制台字符显示,以及图形界面显示二叉树的程序,都比较粗糙,有时间再改进吧,还是能用的,呵呵。
另外代码都是用C++写的,以前用过MFC做界面,但是感觉实在不爽,不喜欢非控制台程序,不喜欢windows,只喜欢简洁的东西,于是考虑使用PYTON,用PYQT,刚弄懂了点皮毛,不过写小程序足够了,感觉很爽,不用编译大大提高效率啊,通过使用swig做胶水可以很简单的将c++的代码做成动态链接库,在python 界面代码中自由调用该模块。
大概效果如下
1.控制台字符显示
Please input below
0 1 3 7 -1 -1 8 -1 -1 4 9 -1 -1 10 -1 -1 2 5 11 -1 -1 12 -1 -1 6 13 -1 -1 14 -1 -1
0
1 2
3 4 5 6
7 8 9 10 11 12 13 14
Inorder travel thread tree
7 3 8 1 9 4 10 0 11 5 12 2 13 6 14
1 2 3 -1 -1 4 5 -1 -1 6 -1 -1 7 -1 8 9 -1 -1 10 -1 -1
Inorder threading no rec
Finished inorder threading
Using Triats method we are printing thread tree now
1
2 7
3 4 8
5 6 9 10
Inorder travel thread tree
3 2 5 4 6 1 7 9 8 10
2.图形界面显示二叉树,以及简单表达式求值
字符二叉树显示代码
采用队列,广度优先遍历二叉树,主要要注意到输出的字符也是要占位的。
template <typename T> class LevelData { public: LevelData ( T *node = NULL, int level = 0 ) : m_node ( node ), m_level ( level ) {} T * node() const { return m_node; } int level() const { return m_level; } private: T *m_node; int m_level; }; //travel fowllow level bread first travel template<typename U, typename T> void BinaryTree<U,T>::LevelOrderTravel ( T *root ) { queue< LevelData<T> > q; q.push ( LevelData<T> ( root,0 ) ); while ( !q.empty() ) { LevelData<T> current_data = q.front(); q.pop(); Visit ( current_data.node() ); if ( current_data.node()->left() ) q.push ( LevelData<T> ( current_data.node()->left(),current_data.level() + 1 ) ); if ( current_data.node()->right() ) q.push ( LevelData<T> ( current_data.node()->right(),current_data.level() + 1 ) ); } } template <typename T> class LevelPrintData : public LevelData<T> { public: LevelPrintData ( T *node = NULL, int level = 0 , int pos = 0 ) :LevelData<T> ( node, level ), m_pos ( pos ) {} int pos() const { return m_pos; } private: int m_pos; }; //开始的方法思路是正确的但是忽略了输出字符要占位置至少一个空格 //spacing 是这样的 最底层的spacing最小 //spacing 一定是显示字符(所有的显示字符给于相同的占位如2个空格) //设字符占位为a //最底层 sapcing = a //则往上依次 spacing = 3a spacing = 7a //spacing = 2*spacing_pre+1 //最底层width_spacing 取 1, 3, 5... //b 2b+1 2(2b+1)+1 //a0 = b a1 = 2a0 + 1 a2 = 4a0 + 2 + 1 an = 2^n*a0 + 2^n - 1 = 2^n(a0 + 1) - 1 //a0 level = depth -1 //a(depth -1) level = 0 //level + n = depth - 1 //n = depth - 1 - level template<typename U, typename T> void BinaryTree<U,T>::PrintTree(const int size, const int width_spacing, const int height_spacing) { return PrintTree(size, width_spacing, height_spacing, typename node_traits<T>::node_category()); } template<typename U, typename T> void BinaryTree<U,T>::PrintTree(const int size, const int width_spacing, const int height_spacing, normal_node_tag) { assert(width_spacing % 2 == 1); if (IsEmpty()) return; int depth = Depth(); //每层第一个节点相对于树的最左边起始位置的偏移量 init_spacing int node_num = 1 << (depth - 1); //最底层的节点数目,按完全满二叉树算 int init_spacing = (((node_num - 1) * width_spacing + node_num - 1) / 2) * size; T *p = root(); int sum = 0; while(p->left()) { p = p->left(); sum++; } //最左边高度为depth-sum的子树没有,可以将root的中心位置相应左移 int node_num2 = 1 << (depth - sum - 1); int init_spacing2 = (((node_num2 - 1) * width_spacing + node_num2 - 1) / 2) * size; init_spacing -= init_spacing2; queue< LevelPrintData<T> > q; q.push (LevelPrintData<T>(root(), 0, init_spacing)); int current_level = 0; //每层中两个相邻节点的spacing spacing_level int spacing_level = ((1 << (depth - 1 - (current_level + 1))) * (width_spacing + 1) - 1) * size; int pre_pos = -1; int move_num; int left_shift, right_shift; while (!q.empty()) { LevelPrintData<T> current_data = q.front(); q.pop(); if (current_data.level() > current_level) { PrintEmptyLine(height_spacing); current_level = current_data.level(); //是下一层的间距the spacing level for current_level + 1 //对于最底层其实是不需要再求底下的spacing的,因为没有孩子了 spacing_level = ((1 << (depth - 1 - (current_level + 1))) * (width_spacing + 1) - 1) * size; pre_pos = -1; } if (pre_pos == -1) //该层首个节点,原来程序pre_pos开始置为0,所以遇到首节点位置是0时出错,因为访问第二个的时候pre_pos还是0 move_num = current_data.pos(); else move_num = current_data.pos() - pre_pos - size; PrintSpace(move_num); VisitData(current_data.node(), size); pre_pos = current_data.pos(); left_shift = ((spacing_level/size + 1) / 2) * size; right_shift = ((spacing_level/size + 1) / 2) * size; if (current_data.node()->left()) q.push(LevelPrintData<T>(current_data.node()->left(),current_data.level() + 1, current_data.pos() - left_shift)); if (current_data.node()->right()) q.push(LevelPrintData<T>(current_data.node()->right(),current_data.level() + 1, current_data.pos() + right_shift)); } cout << endl; }
图形界面二叉树显示代码
由于swig,似乎对于复杂模板,如存在模板参数互相依赖的情况支持不是很好,反正我没弄出来,也许还是了解的不够,所以 我把以前写的比较复杂的,比如节点类会有一个模板继承等等提出一个简单的二叉树节点,和二叉树类,便于swig处理。
1.python写的界面程序
里面的expression 模块是c++代码由swig处理生成。
import sys from PyQt4 import QtCore, QtGui from PyQt4.QtGui import * # expression is a moudule getting from c++ code using swig import expression # TODO self.width() the value returned seemed uncorrect class TreePrinter(QWidget): def __init__(self, tree, parent = None): QWidget.__init__(self, parent) palette = QPalette(self.palette()) palette.setColor(palette.Background, QtCore.Qt.transparent) self.setPalette(palette) self.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents) self.tree = tree self.depth = self.tree.Depth(self.tree.root()) #self.width_spacing = (self.width() - 10) / (1 << (self.depth - 1) - 1) #self.height_spacing = (self.height() - 10) / (self.depth - 1) #self.init_x = self.width() self.width_spacing = (400 - 10) / ((1 << (self.depth - 1))- 1) self.height_spacing = (400 - 50) / (self.depth - 1) self.init_x = 200 self.init_y = 20 def paintEvent(self, event): self.painter = QPainter() self.painter.begin(self) self.painter.setRenderHint(QPainter.Antialiasing) self.painter.setPen(QPen(QColor(255, 0, 0))) # draw the tree self.PrintTreeHelp(self.tree.root(), 0, self.init_x, self.init_y) self.painter.end() def PrintTreeHelp(self, root, cur_level, pos_x, pos_y): if (cur_level == self.depth - 1): self.painter.setPen(QPen(QColor(0, 0, 0))) self.painter.drawText(pos_x, pos_y, root.elem()) self.painter.setPen(QPen(QColor(255, 0, 0))) return spacing_level = (1 << (self.depth - cur_level -2))*self.width_spacing if (root.left()): self.painter.drawLine(pos_x, pos_y, pos_x - spacing_level/2, pos_y + self.height_spacing) self.PrintTreeHelp(root.left(), cur_level+1, pos_x - spacing_level/2, pos_y + self.height_spacing) if (root.right()): self.painter.drawLine(pos_x, pos_y, pos_x + spacing_level/2, pos_y + self.height_spacing) self.PrintTreeHelp(root.right(), cur_level+1, pos_x + spacing_level/2, pos_y + self.height_spacing) #will draw the node value self.painter.setPen(QPen(QColor(0, 0, 0))) self.painter.drawText(pos_x, pos_y, root.elem()) self.painter.setPen(QPen(QColor(255, 0, 0))) class MySubWindow(QtGui.QMainWindow): def __init__(self, tree, parent = None): QtGui.QMainWindow.__init__(self, parent) self.setWindowTitle("Print The Tree") self.textEdit = QtGui.QTextEdit() self.setCentralWidget(self.textEdit) self.setGeometry(QtCore.QRect(610,200,400,400)) self.tree = tree self.print_tree = TreePrinter(tree, self) def resizeEvent(self, event): self.print_tree.resize(event.size()) event.accept() class MyWindow(QtGui.QMainWindow): def __init__(self, parent=None): QtGui.QMainWindow.__init__(self, parent) self.setWindowTitle("GoldenLock Calculator") #self.resize(QtCore.QSize(QtCore.QRect(0,0,400,400).size()).expandedTo(self.minimumSizeHint())) self.setGeometry(QtCore.QRect(200,200,400,400)) self.textEdit = QtGui.QTextEdit() self.setCentralWidget(self.textEdit) calcAction = QtGui.QAction('c&alc', self) self.connect(calcAction, QtCore.SIGNAL('activated()'), self.Calculate) calcAction.setShortcut("Enter"); clearAction = QtGui.QAction('&clear', self) self.connect(clearAction, QtCore.SIGNAL('triggered()'), self.Clear) printTreeAction = QtGui.QAction('&print tree', self) self.connect(printTreeAction, QtCore.SIGNAL('triggered()'), self.PrintTree) menubar = self.menuBar() menubar.addAction(calcAction) menubar.addAction(clearAction) menubar.addAction(printTreeAction) def Calculate(self): if self.textEdit.toPlainText() == '': return infix_string = self.textEdit.document().end().previous().text() self.infix_string = expression.InfixStringNormallize(str(infix_string)) #evaluate the eprsssion value using rec value = expression.EvaluateInfixString(self.infix_string) self.textEdit.insertPlainText("\nvalue: " + str(value)+"\n") def Clear(self): self.textEdit.setText("") self.infix_string='' def PrintTree(self): if self.infix_string: self.tree = expression.string_node_tree() # we will get the binary tree from the self.infix_string and store in self.tree expression.CreateExpressionBinaryTree(self.infix_string, self.tree) self.window2 = MySubWindow(self.tree) self.window2.show() if __name__ == '__main__': app = QtGui.QApplication(sys.argv) window = MyWindow() window.show()
sys.exit(app.exec_())
2. c++代码部分,底层数据结构及基本函数算法。
simple_binary_tree.h
#ifndef _SIMPLE_BINARY_TREE_H #define _SIMPLE_BINARY_TREE_H using namespace std; namespace binary_tree{ struct normal_node_tag {}; //标记普通的二叉树节点,带左右孩子指针 struct with_parent_node_tag: public normal_node_tag{}; //标记带parent域的二叉树节点 struct with_thread_node_tag: public normal_node_tag {}; //标记利用空指针记录线索的二叉树节点 template <typename NodeT> struct node_traits { typedef typename NodeT::node_category node_category; }; template <typename U, typename T> class BinarySearchTree; //forward declaration of BinarySearchTree //定义一个独立的二叉树节点类,因为swig似乎不支持嵌套模板 template<typename U> class SimpleBinaryTreeNode { public: typedef SimpleBinaryTreeNode T; typedef normal_node_tag node_category; template <typename type1, typename type2> friend class BinaryTree; template <typename type1, typename type2> friend class BinaryTreeHelper; template <typename type1, typename type2> friend class BinarySearchTree; SimpleBinaryTreeNode() : m_left (NULL), m_right (NULL) {} SimpleBinaryTreeNode(const U& elem, T * left = NULL, T * right = NULL) : m_elem (elem), m_left (left), m_right (right) {} //accessors //return the current node data 如果不加const修饰也要注意是只读的因为返回的是临时变量 //ok 为了方便,暂时先不加了 U elem() const { // TODO 如果 const U elem() const 那么后面用到elem()的函数参数也要const return m_elem; //BinaryTree的也要用const 比如 travel(T* root) { travel root->left()} } //是错误的,因为root->left()返回const 然后 travel(root->left()),试图转为non const //return left child T * left() const { // T const * left() // const pointer non const data it points return m_left; } //return right child T * right() const { return m_right; } // return 1 if the node is a leaf bool IsLeaf() const { return !m_left && !m_right; } //mutators //set current node data void set_elem(const U & elem) { m_elem = elem; } //set left child tree void set_left(T *left) { m_left = left; } //set right child tree void set_right(T *right) { m_right = right; } private: U m_elem; T *m_left; T *m_right; }; template<typename U, typename T> class SimpleBinaryTree { public: template <typename type1, typename type2> friend class BinaryTreeHelper; //要用到m_root //如果改变一下三叉树建立函数,不用引用的化,left = Create.() 这样就可以不用私有变量 SimpleBinaryTree ( T *root = NULL ) : m_root ( root ) {} //~BinaryTree(){cout << "destruct" << endl;} virtual ~SimpleBinaryTree() { DeleteBinaryTree(m_root); } //accessors //return root of the tree T* root() const { return m_root; } //rturn left child of the current node T* left_child_of ( T *current ) const { return current->left(); } //return right child of the current node T* right_child_of ( T *current ) const { return current->right(); } //判空 bool IsEmpty() const { return ( m_root==NULL ); } // TODO temp change 如果你希望在别的地方直接建树,就需要public它,否则要不断加friend但这样比较危险 void set_root(T *root) { m_root = root; } void DeleteBinaryTree(T *root) { if ( root ) { DeleteBinaryTree ( root->left() ); DeleteBinaryTree ( root->right() ); delete root; } } virtual int Depth(T *root) { if (root) { int left_depth = Depth(root->left()); int right_depth = Depth(root->right()); if (left_depth >= right_depth) return left_depth + 1; else return right_depth + 1; //int depth = left_depth >= right_depth ? left_depth +1 , right_depth + 1; } else { return 0; } } protected: T* m_root; }; } // end of namespace binary_tree
#endif // end of _SIMPLE_BINARY_TREE_H
expression_py.h
#ifndef _EXPRESSION_PY_H_ #define _EXPRESSION_PY_H_ #include <string> using namespace std; namespace binary_tree { //二叉树前置声明 template<typename T> class BinaryTreeNode; template<typename U, typename T> class BinaryTree; template<typename T> class SimpleBinaryTreeNode; template<typename U, typename T> class SimpleBinaryTree; } //TODO 能否让string支持引用,从而不用改写函数 //但似乎需要让swig知道string 的定义 ,麻烦 //TODO 还是要改回,使用BinaryTree 而不是 SimpleBinaryTree //但是仍然用simple node,用 binary_tree.h //TODO 将expression.h 中的 CreateExpressionBinaryTree //改写为模板函数,以tree的类型为模板参数 namespace expression_py { //中缀表达式带括号转后缀,支持浮点 //用户输入有错误会识别出来报错,不做修正 //为了python接口,函数参数调整 //typedef std::string string; string Infix2Suffix(const string &infix_string); //判断给出的中缀表达式是否正确,括号匹配正确,无特殊字符,否则报错 //为了python接口,函数参数调整 string InfixStringNormallize(const string &infix_string); bool PrintExpressionTree(const string &infix_string); typedef binary_tree::SimpleBinaryTreeNode<string> Node; typedef binary_tree::SimpleBinaryTree<string, Node> Tree; bool CreateExpressionBinaryTree(const string& infix_string, Tree &binary_tree); } //下面的接口函数直接使用expression namespace下的 //后缀表达式求值,注意后缀表达式是用" "隔开的 namespace expression { // well 啊 这里不支持重载 //bool Infix2Suffix(string infix_string, string &suffix_string); double EvaluateSuffixString(const string &suffix_string); //中缀表达式直接求值,要求中缀表达式格式正确,且按空格隔开 //递归求解 double EvaluateInfixString(const string& infix_string); double EvaluateInfixString2(const string& infix_string); }
#endif //end of _EXPRESSION_PY_H_
expression_py.cc
#include "expression_py.h" #include "expression.h" #include "binary_tree.h" //#include "print_tree_help.h" using std::string; namespace expression_py { //中缀表达式带括号转后缀,支持浮点 string Infix2Suffix(const string &infix_string) { string s; expression::Infix2Suffix(infix_string, s); return s; } //注意如果在同一名字域下,这个函数按重载处理会有问题的! //必然 string s; InfixStringNormalize(s) string InfixStringNormallize(const string &infix_string) { string s(infix_string); expression::InfixStringNormallize(s); return s; } bool PrintExpressionTree(const string &infix_string) { using namespace binary_tree; string s(infix_string); if (!expression::InfixStringNormallize(s)) return false; expression::Tree my_binary_tree; expression::CreateExpressionBinaryTree(s, my_binary_tree); //my_binary_tree.PrintTree(); return true; } //string 不能用引用的原因是你在python里面当前 //无法 a = string('abc') 看不到string 的定义 //所以无法调用带string&的函数 bool CreateExpressionBinaryTree(const string& infix_string, Tree &binary_tree) { string s(infix_string); if (!expression::InfixStringNormallize(s)) return false; expression::CreateExpressionBinaryTree(s, binary_tree); }
} //end of namespace expression_py
expression.i
%module expression
%include "std_string.i" %{ /* Includes the header in the wrapper code */ //%rename() Bar::spam; #include "simple_binary_tree.h" #include "expression_py.h" %} /* Parse the header file to generate wrappers */ %include "simple_binary_tree.h" %include "expression_py.h" //%template(string_node) binary_tree::BinaryTreeNode<string>; //%template(string_node_tree) binary_tree::BinaryTree<string,string_node>; %template(string_node) binary_tree::SimpleBinaryTreeNode<string>; %template(string_node_tree) binary_tree::SimpleBinaryTree<string,binary_tree::SimpleBinaryTreeNode<string> >;