数据结构练手04 二叉树之链表实现
晚上和朋友出去小饮了几杯,调侃了下blablabla。。。现在有点晕乎乎的,文章可能很凌乱,算是很抱歉。
下午实现了下链表形式的二叉树,顿时感觉数据结构的实现,理论和实际的差距还是很大的。要走的路还是很长远的。总结下,就是递归很重要,这个是解决树问题的核心钥匙!
对于二叉树,很多实现是基于排序二叉树的线性表示,即可以根据父节点的索引直接定位到子节点的位置,即 parent*2 = leftChild, parent*2+1=rightChild等等,一切可以用公式表示。
本文,我们采用指针形式来实现。
每个数据结构的实现,首要是实现结点类的设计。根据需求,我们知道它有三个指针域,分别指向父节点,左结点,右结点,还有一个数据域。另外需要个性化一些构造函数,满足不同的需求。
1 template <class T> 2 struct bNode{ 3 T datum; 4 bNode<T>* left; 5 bNode<T>* right; 6 bNode<T>* parent; 7 bNode() : datum(0),left(NULL),right(NULL),parent(NULL){} 8 bNode(const T& x) : datum(x), left(NULL), right(NULL),parent(NULL){} 9 ~bNode(){left = NULL; right = NULL; parent = NULL;} 10 };
实现完结点结构后,后序的工作是需要定制二叉树类。根据需求,一般来说要实现:查找,插入,删除,遍历,求长度,深度,长度等操作。同时,很重要的是要有一个结点类的head指针提供数据访问的接口。我们把类设计如下:
template<class T> class BiTree{ protected: bNode<T>* head; // 头结点,它的右结点指向根节点 int totalSize; bool _search(const T& x, bNode<T>* n) const; // 内部接口。 public: BiTree() : totalSize(0) {head = new bNode<T>();} ~BiTree(); void clear(); void init(const T array[], int len, bNode<T>* n = NULL, int beg=1); // 根据数组来初始化一个满二叉树 void preOrder(const bNode<T>* node) const; // 遍历次序 void inOrder(const bNode<T>* node) const; void postOrder(const bNode<T>* node) const; void preOrder() const; void inOrder() const; void postOrder() const; bNode<T>*& root() const { return head->right;} // 这里为啥要返回一个指针的引用呢? bNode<T>* insert(bNode<T>* &node, const T& x, INSERTTYPE t = RIGHT); // 左插or右插 BiTree<T>& erase(bNode<T>* &node, T& carry); bool search(const T& x) const; // 调用内部_search接口,默认从根结点开始 int size(bNode<T>*) const; bool isEmpty(){return size==0;} int depth(bNode<T>* node) const; int height(bNode<T>* node) const; };
其中有七个地方用到了递归,分别是三个遍历,两个求长度、高度,搜索,还有个初始化:
template<class T> void BiTree<T>::init( const T array[], int len, bNode<T>* n, int beg) { if (n == NULL) n = head; bNode<T>* p=NULL; if(beg<=len){ if(beg % 2 == 0){ // 要是左结点,则左插,并且递归调用 p = insert(n, array[beg-1],LEFT); init(array, len, p, 2*beg); // 递归到左结点 init(array, len, p, 2*beg+1);// 递归到右结点 }else{ // 右结点,同时递归调用 p = insert(n, array[beg-1], RIGHT); init(array, len, p, 2*beg); init(array, len, p, 2*beg+1); } } } template<class T> BiTree<T>::~BiTree() { clear(); delete head; } template<typename T> void BiTree<T>::clear() // 销毁一棵树 { T tmp; int s = totalSize; for(int i=0; i<s; ++i){ erase(root(),tmp); } } template<class T> bool BiTree<T>::_search(const T& x, bNode<T>* n) const { if(n == NULL) return false; if(n->datum == x) return true; return _search(x,n->left) || _search(x,n->right); 递归搜索左右结点 } template<class T> bool BiTree<T>::search(const T& x) const // 调用内部接口,默认从根结点开始 { return _search(x, root()); } template<typename T> bNode<T>* BiTree<T>::insert(bNode<T>* &node, const T& x, INSERTTYPE t /* = INSERTTYPE::RIGHT */) // 插入到某结点的左or右 { bNode<T>* inode = new bNode<T>(x); if( t == INSERTTYPE::LEFT){ inode->left = node->left; if(node->left != NULL) node->left->parent = inode; node->left = inode; } if( t == INSERTTYPE::RIGHT){ inode->right = node->right; if(node->right != NULL) node->right->parent = inode; node->right = inode; } inode->parent = node; ++totalSize; return inode; } template<typename T> // 用到了一个技巧,就是交换欲销毁结点和某个子孙叶结点的值,然后销毁该子孙结点,这样树结构基本不变 BiTree<T>& BiTree<T>::erase(bNode<T>* &node, T& carry) { carry = node->datum; bNode<T>* p = node; while(p && ((p->left != NULL) || (p->right != NULL))){ if(p->left != NULL){ p = p->left; continue; } if(p->right != NULL){ p = p->right; } } node->datum = p->datum; if(p == p->parent->left){ p->parent->left = NULL; }else{ p->parent->right = NULL; } delete p; --totalSize; return *this; } template<class T> // 某个结点的大小 int BiTree<T>::size(bNode<T>* node) const { if (node !=NULL) return 1 + size(node->left)+size(node->right); return 0; } template<class T> // 深度 int BiTree<T>::depth(bNode<T>* node) const { int dep = 0; while(node!=root()){ dep++; node = node->parent; } return dep; } template<class T> // 高度 int BiTree<T>::height(bNode<T>* node) const { if (((node != NULL) && (node->left == NULL) && (node->right == NULL)) || (node == NULL)) { return 0; } return 1 + ::max(height(node->left), height(node->right)); } template<typename T> //先序 void BiTree<T>::preOrder(const bNode<T>* node) const { if(node != NULL){ cout << node->datum << " "; //根 preOrder (node->left); // 左结点 preOrder (node->right); //右结点
} } template<typename T> void BiTree<T>::preOrder() const { preOrder(root()); cout << endl; } //中序 template<typename T> void BiTree<T>::inOrder(const bNode<T>* node) const { if(node != NULL){ inOrder (node->left); cout << node->datum << " "; inOrder (node->right); } } template<typename T> void BiTree<T>::inOrder() const { inOrder(root()); cout << endl; } // 后序 template<typename T> void BiTree<T>::postOrder(const bNode<T>* node) const { if(node != NULL){ postOrder (node->left); postOrder (node->right); cout << node->datum << " "; } } template<typename T> void BiTree<T>::postOrder() const { postOrder(root()); cout << endl; }
测试代码如下:
1 #include <iostream> 2 #include "biTree.h" 3 using namespace std; 4 int main() 5 { 6 BiTree<int> btree; 7 int a[10]; 8 for(int i=0; i<10; ++i){ 9 a[i] = i+1; 10 } 11 btree.init(a,10); 12 cout << "preorder: "; 13 btree.preOrder (); 14 cout << "inorder: "; 15 btree.inOrder(); 16 cout << "postorder: "; 17 btree.postOrder(); 18 cout << "size of root is " << btree.size(btree.root())<< endl; 19 cout << "depth of root is " << btree.depth(btree.root())<< endl; 20 cout << "height of root is " << btree.height(btree.root()) << endl; 21 cout << "is 40 exist? " << btree.search(40) << endl; 22 cout << "is 6 exist " << btree.search(6) << endl; 23 }
。
路漫漫其修远兮,吾将上下而求索