二叉树常用算法总结
2010-10-24 20:42
357 查看
最近在复习算法,做了一下编程之美上面关于二叉树的三道题,感觉顺便就把二叉树总结下算了,于是就有了这篇文章,这些算法有些是自己思考出来的,比如重建二叉树,有些是以前的知识还有些是看了网上其它大牛总结而成,现在贴出来分享,希望大家指正。感觉算法的学习重要的不是算法本身而是算法背后的思想,就是一些本质上的东西,或者说思考的过程,这些才是真正重要的。对于二叉树来说,最重要的是它的递归特性,这是由它的定义决定的。写了这么多二叉树的算法,思考了下,对于二叉树的相关问题,首先要考虑的就是是否能应用它的递归性质。
二叉树常用算法总结
二叉树常用算法总结
////////////////////////////////////////////////////////////////////////// // 二叉树相关算法总结 // 2010.10.22 // // By HappyAngel // 这里的算法部分来自自己的思考原创,部分来自对网上的总结,收获不少。 ////////////////////////////////////////////////////////////////////////// #include <iostream> #include <stack> #include <cstdlib> #include <queue> using namespace std; typedef struct TreeNode { TreeNode* pLeftNode; TreeNode* pRightNode; char nodeName; bool flag; //for post-order traverse int level; //for level traverse }TreeNode, * PTreeNode; //recurisively create binary tree PTreeNode CreateBinaryTree(void) { char c; cin>>c; PTreeNode pRoot; if('#' == c) { return NULL; } else { pRoot = new TreeNode; pRoot->nodeName = c; pRoot->flag = false; pRoot->level = 0; pRoot->pLeftNode = CreateBinaryTree(); pRoot->pRightNode = CreateBinaryTree(); } return pRoot; } void DeleteTree(PTreeNode pRoot) { if(NULL != pRoot) { DeleteTree(pRoot->pLeftNode); DeleteTree(pRoot->pRightNode); delete pRoot; } } void access(const TreeNode& node) { cout<<node.nodeName<<" "; } //recursely preorder void PreOrderTraverse(PTreeNode pNode) { if(pNode != NULL) { access(*pNode); PreOrderTraverse(pNode->pLeftNode); PreOrderTraverse(pNode->pRightNode); } } //recursively inorder void InOrderTraverse(PTreeNode pNode) { if(pNode != NULL) { InOrderTraverse(pNode->pLeftNode); access(*pNode); InOrderTraverse(pNode->pRightNode); } } //recursely postorder void PostOrderTraverse(PTreeNode pNode) { if(pNode != NULL) { PostOrderTraverse(pNode->pLeftNode); PostOrderTraverse(pNode->pRightNode); access(*pNode); } } ////////////////////////////////////////////////////////////////////////// //非递归前序遍历二叉树 //思路:一样,要用堆栈保存左右子树,保存顺序为先右后左。 // ////////////////////////////////////////////////////////////////////////// void NonRecursivePreOrder(PTreeNode pNode) { stack<PTreeNode> s; PTreeNode p = pNode; while(NULL != p || !s.empty()) { access(*p); if(NULL != p->pRightNode) s.push(p->pRightNode); if(NULL != p->pLeftNode) s.push(p->pLeftNode); if(!s.empty()) { p = s.top(); s.pop(); } else p = NULL; } } ////////////////////////////////////////////////////////////////////////// // 非递归中序遍历二叉树 //思路:首先,由于是去掉了递归,必须保存以前访问过的中间节点,所以必须有堆栈。 //其次根据中序遍历的定义,是左中右,所以要找到最左边的叶子节点,并用堆栈保存根节点。 //访问完左节点后(NULL也算一个左节点),出栈并访问中间节点,最后访问右节点,并循环之。 //结束条件:堆栈为空或者指针为空,之所以加入指针为空是为了在最开始的时候不必把根节点压入堆栈。 //可以对所有节点一视同仁,简化判断。因此在中间要判断堆栈不为空才弹。 ////////////////////////////////////////////////////////////////////////// void NonRecursiveInOrder(PTreeNode pNode) { stack<PTreeNode> s; PTreeNode p = pNode; while(NULL != p || !s.empty()) { while(NULL != p) { s.push(p); p = p->pLeftNode; } if(!s.empty()) { p = s.top(); s.pop(); access(*p); p = p->pRightNode; } } } ////////////////////////////////////////////////////////////////////////// //非递归后序遍历二叉树 //用一个标记标志是否是从右子树回到中间节点,这样,还是先访问左,再访问右, //最后访问中间。 // // ////////////////////////////////////////////////////////////////////////// void NonRecursivePostOrder(PTreeNode pNode) { stack<PTreeNode> s; PTreeNode p = pNode; while(NULL != p || !s.empty()) { while(NULL != p) { s.push(p); p = p->pLeftNode; } if(!s.empty()) { p = s.top(); if(p->flag) { access(*p); s.pop();//访问完了再出栈 p = NULL; //右子树已经访问 } else { p->flag = true; p = p->pRightNode; //访问右子树 } } } } ////////////////////////////////////////////////////////////////////////// //根据先序和中序遍历的结果重建二叉树 此题原自编程之美3.9 //输入:pPreOrder, pInOrder, nTreeLen //输出:pRoot //算法思想:先序集合不变,中序集合是变化的,每次递归做三件事:找根节点 FindRoot ,即查找先序集合中第一个 //同时又在中序集合的节点;第二步,根据根节点切分中序集合 Split ,实质上是把中序分为左右子树集合 //;第三步,递归构建左子树与右子树; //二叉树最本质的特性就是递归,这是由它的定义决定的,所以递归是解决二叉树问题的神器。 ////////////////////////////////////////////////////////////////////////// const int maxLen = 20; void Split(char c, char* pInOrder, char** pInOrder1, char** pInOrder2) { *pInOrder1 = new char[maxLen]; *pInOrder2= new char[maxLen]; memset(*pInOrder1,0,sizeof(char)*maxLen); memset(*pInOrder2,0,sizeof(char)*maxLen); int j1=0; int j2=0; int i=0; for(; NULL != pInOrder[i]; i++) { if(c != pInOrder[i]) { (*pInOrder1)[j1] = pInOrder[i]; j1++; } else break; } i++; for(; NULL != pInOrder[i]; i++) { (*pInOrder2)[j2] = pInOrder[i]; j2++; } } char FindRoot(char* pPreOrder, char* pInOrder) { char c = NULL; for(int i=0; NULL != pPreOrder[i]; i++) { for(int j=0; NULL != pInOrder[j]; j++) { if(pPreOrder[i] == pInOrder[j]) { c = pPreOrder[i]; return c; } } } return c; } PTreeNode CreateNode(char c) { PTreeNode p = new TreeNode; p->nodeName = c; p->pLeftNode = NULL; p->pRightNode = NULL; return p; } PTreeNode Rebuild2(char* pPreOrder, char* pInOrder) { if(0 == strlen(pInOrder)) { return NULL; } //找根节点 char c = FindRoot(pPreOrder,pInOrder); PTreeNode p = CreateNode(c); char* pInOrder1 = NULL; char* pInOrder2 = NULL; //根据根节点切分中序集合 Split(c,pInOrder,&pInOrder1,&pInOrder2); //重建左子树 p->pLeftNode = Rebuild2(pPreOrder,pInOrder1); //重建右子树 p->pRightNode = Rebuild2(pPreOrder, pInOrder2); delete pInOrder1; delete pInOrder2; return p; } void Rebuild(char* pPreOrder, char* pInOrder, int nTreeLen, PTreeNode* pRoot) { *pRoot = Rebuild2(pPreOrder,pInOrder); } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// //层次遍历二叉树 //思路:应用队列即可轻松实现,在实现上保持与其他遍历代码的一致性,主要体现在 //循环终止条件上 // ////////////////////////////////////////////////////////////////////////// void LevelTraverse(PTreeNode root) { queue<PTreeNode> q; PTreeNode p = root; while(NULL != p || !q.empty()) { if(!q.empty()) { p = q.front(); q.pop(); } access(*p); if(NULL != p->pLeftNode) { q.push(p->pLeftNode); } if(NULL != p->pRightNode) { q.push(p->pRightNode); } p = NULL; //to make the program ends after accessing the last node } } ////////////////////////////////////////////////////////////////////////// //非递归打印二叉树中某层次的节点 // // ////////////////////////////////////////////////////////////////////////// int PrintNodeAtLevel(PTreeNode root, int level) { queue<PTreeNode> q; PTreeNode p = root; if(root == NULL || level < 0) return 0; int l = 0; while(NULL != p || !q.empty()) { if(!q.empty()) { p = q.front(); q.pop(); } if(level == p->level) { access(*p); } if(NULL != p->pLeftNode) { q.push(p->pLeftNode); p->pLeftNode->level = p->level+1; } if(NULL != p->pRightNode) { q.push(p->pRightNode); p->pRightNode->level = p->level+1; } p = NULL; //to make the program ends after accessing the last node } return 1; } /////////////////////////////////////////////////////////////////////////// //递归按层打印二叉树,此法把记录层次的任务交给函数本身,乃递归的妙用 // ////////////////////////////////////////////////////////////////////////// int RecursivePrintAtLevel(PTreeNode root, int level) { if(level < 0 || root == NULL) return 0; if(level == 0) { access(*root); return 1; } return RecursivePrintAtLevel(root->pLeftNode,level-1) + RecursivePrintAtLevel(root->pRightNode,level-1); } ////////////////////////////////////////////////////////////////////////// //递归求二叉树的深度 // // ////////////////////////////////////////////////////////////////////////// int RecursiveGetTreeDepth(PTreeNode root) { if(NULL == root) return 0; else { int l = RecursiveGetTreeDepth(root->pLeftNode); int r = RecursiveGetTreeDepth(root->pRightNode); return (l > r ? l+1 : r+1); } } int main(void) { PTreeNode p = CreateBinaryTree(); DeleteTree(p); getchar();//skip enter getchar();//pause return 0; }
相关文章推荐
- hadoop常用的压缩算法总结和实验验证
- Anti-alias的前世今生-常用反走样算法总结
- javascript常用数组算法总结
- OS中常用的调度算法总结 (转)
- java常用算法总结
- 操作数组的常用算法总结
- 常用 大数据量、海量数据处理 方法 / 算法总结
- Java常用算法总结
- 【转载】常用 大数据量、海量数据处理 方法 / 算法总结
- 五大常用算法总结
- String 常用方法最优算法实现总结 (一)
- 常用 大数据量、海量数据处理 方法 / 算法总结
- String 常用方法最优算法实现总结 (二)
- php常用算法总结
- javascript常用数组算法总结
- 【算法总结】Binary Tree & Binary Search Tree 二叉树
- javascript中的一些常用的数组算法总结
- 【算法基础个人常用总结】<-------持续更新------->
- 机器学习之常用算法总结
- 面试中常用的算法总结