声明:本博客为原创博客,未经允许,不得转载!小伙伴们如果是在别的地方看到的话,建议还是来csdn上看吧(原文链接地址为http://blog.csdn.net/bettarwang/article/details/12879459),毕竟在csdn上看代码和提问、讨论都更方便。
前面已经罗列了二叉树的建立、遍历的各种方法,但是二叉树作为一种重要的数据结构,它还有一些很常用的操作,比如寻找结点,删除二叉树中的结点,复制二叉树,判断二叉树是否相似、相等,等待。
空谈误国,直接上代码。代码中有相应的说明。
#ifndef OTHEROPERATIONS_H #define OTHEROPERATIONS_H #include <stack> #include <deque> #include <queue> #include "BTNode.h" ////////////////////////////////////这个函数用于在普通二叉树中寻找节点。 template<class T> BTNode<T>*findNodeInBinaryTree(BTNode<T>*root,T item,bool recursionFlag) { if(recursionFlag) //当然,采用递归方法时,也可以利用某种遍历方法。比如这里的其实就是前序遍历方法。 { if(root!=NULL) { if(root->data==item) return root; else { BTNode<T>*possibleLeft,*possibleRight; possibleLeft=findNodeInBinaryTree(root->leftChild,item,true); possibleRight=findNodeInBinaryTree(root->rightChild,item,true); if(possibleLeft!=NULL) return possibleLeft; else if(possibleRight!=NULL) return possibleRight; else return NULL; } } else //其实这样写不好,这样写很容易忘记return NULL这一种情况。所以比较好的做法是先写简单的情形。 return NULL; } else //采用非递归的方法。其实也就是非递归的遍历方法。这里采用非递归的中序遍历方法。 { BTNode<T>*p=root; stack<BTNode<T>*>nodeStack; do { while(p!=NULL) { nodeStack.push(p); p=p->leftChild; } p=nodeStack.top(); nodeStack.pop(); if(p->data==item) return p; p=p->rightChild;//别忘了这一步,要不然遍历不到右子树。 } while (!nodeStack.empty()||p!=NULL); //如果上面都结束了还没有返回的话,那要么是找不到,要么root==NULL,那么就应该返回NULL。 return NULL; } } ////////////////////////////////////能否写一个函数可以同时找到相应的节点及其双亲结点呢?其实是可以的,而且只要再增加一个变量即可。 template<class T> void findNodeAndParentInBT(BTNode<T>*root,T item,BTNode<T>*&node,BTNode<T>*&parentNode,bool recursionFlag) { if(recursionFlag) { if(root==NULL) { node=NULL; parentNode=NULL; return; } else { if(item==root->data) { node=root; parentNode=NULL; return; } else { if(root->leftChild!=NULL) { if(root->leftChild->data==item) { node=root->leftChild; parentNode=root; return; } else //findNodeAndParentInBT(root->leftChild,item,possibleLeftNode,possibleLeftParent,true); findNodeAndParentInBT(root->leftChild,item,node,parentNode,true); } ///////////////////////////////这其实就类似中序遍历,即先查找左子树,然后查找右子树。 if(root->rightChild!=NULL) { if(root->rightChild->data==item) { node=root->rightChild; parentNode=root; return; } else //findNodeAndParentInBT(root->rightChild,item,possibleRightNode,possibleRightParent,true); findNodeAndParentInBT(root->rightChild,item,node,parentNode,true); } } } } else//下面运用非递归方法。 { if(root==NULL) { node=NULL; parentNode=NULL; return; } else { if(root->data==item) { node=root; parentNode=NULL; return; } else { stack<BTNode<T>*>nodeStack; stack<BTNode<T>*>parentStack; nodeStack.push(root); parentStack.push(NULL); BTNode<T>*p1=root; BTNode<T>*p2=p1->leftChild; //BTNode<T>*p; do { while(p2!=NULL) { nodeStack.push(p2); parentStack.push(p1); p1=p1->leftChild; p2=p2->leftChild; } p2=nodeStack.top(); p1=parentStack.top(); nodeStack.pop(); parentStack.pop(); ////////////////// if(p2->data==item) { node=p2; parentNode=p1; return; } /////////////////如果不满足这个条件的话就要转向右子树。 p1=p2; //加上这句这重要,一定要记得时候让p1是p2的双亲结点。这点要时刻保持不变。 p2=p2->rightChild; } while (!nodeStack.empty()||p2!=NULL); //注意:此时这里的控制条件不能写成(p2!=NULL&&p1!=NULL),记住,p1只是我们附加的变量,它变不起控制程序流程的作用。 } } } } //////////////////////////////很简单,只要按照寻找父结点的方法来寻找就行了。 template<class T> void findNodeAndParentInSortTree(BTNode<T>*root,T item,BTNode<T>*&node,BTNode<T>*&parentNode,bool recursionFlag) { if(recursionFlag) { if(root==NULL) { node=NULL; parentNode=NULL; return; } else { if(root->data==item) //注意这种情况并不多余,因为可能要找的那个节点就是根结点呢。 { node=root; parentNode=NULL; return; } else if(item<root->data) { if(root->leftChild!=NULL) { if(item==root->leftChild->data) { node=root->leftChild; parentNode=root; return; } else findNodeAndParentInSortTree(root->leftChild,item,node,parentNode,true); } ////其实这个else完全可以不要,因为实际上if(root==NULL)那里就把这种情况考虑进去了,等下测试一下就知道。 /* else { node=NULL; parentNode=NULL; return; } */ } else { if(root->rightChild!=NULL) { if(item==root->rightChild->data) { node=root->rightChild; parentNode=root; return; } else findNodeAndParentInSortTree(root->rightChild,item,node,parentNode,true); } ///////////这个else语句也可以不要的。 /* else { node=NULL; parentNode=NULL; } */ } } } else { if(root==NULL) { node=NULL; parentNode=NULL; return; } else { if(item==root->data) { node=root; parentNode=NULL; return; } else { BTNode<T>*p=root; while(p!=NULL) { if(item<p->data) { if(p->leftChild!=NULL) { if(item==p->leftChild->data) { node=p->leftChild; parentNode=p; return; } else { p=p->leftChild; //continue;//这个continue好像是多余的。 } } //如果while里面没加那个判断条件的话,这里就要加一个else条件。 } else { if(p->rightChild!=NULL) { if(item==p->rightChild->data) { node=p->rightChild; parentNode=p; return; } else { p=p->rightChild; } } } } //////////////////////////如果没有找到的话,那就只能是返回NULL了。 node=NULL; parentNode=NULL; return; } } } } ////////////////////////////////这个函数用于在二叉查找树中寻找节点。 template<class T> BTNode<T>*findNodeInSortTree(BTNode<T>*root,T item,bool recursionFlag) { //////////////////////////////////当然可以采用某种遍历的方法来寻找节点,但那只是针对一般的二叉树而言,对于二叉排序树没有必要这样。 if(recursionFlag) { if(root==NULL) return NULL; else { if(item==root->data) return root; else if(item<root->data) return findNodeInSortTree(root->leftChild,item,true); else return findNodeInSortTree(root->rightChild,item,true); } } else { BTNode<T>*p=root; while(1) { if(p==NULL) return NULL; if(item==p->data) return p; else if(item<p->data) { p=p->leftChild; } else p=p->rightChild; } } } ////////////////////////////////这个函数用于在二叉查找树中寻找某个节点的双亲节点。 template<typename T> BTNode<T>*findParentNodeInSortTree(BTNode<T>*root,T item,bool recursionFlag)//还要写一个findParentNodeInSortTree(BTNode<T>*root,BTNode<T>*node);的函数。要找的是item的父结点。 { if(recursionFlag) { if(root==NULL) return NULL; else { if(item==root->data) return NULL; else if(item<root->data) { if(root->leftChild!=NULL) { if(item==root->leftChild->data) return root; else return findParentNodeInSortTree(root->leftChild,item,true); } else return NULL; } else { if(root->rightChild!=NULL) { if(item==root->rightChild->data) return root; else return findParentNodeInSortTree(root->rightChild,item,true); } else return NULL; } } } else { BTNode<T>*p=root; while(1) { if(p==NULL||root->data==item) //会分这几种情况很重要。特别注意的是虽然要跟p的孩子结点进行值的比较,但是分情况却还是要根据item与p->data的关系来分,因为这决定了要找的结点会在哪一边。 return NULL; else if(item<p->data) { if(p->leftChild!=NULL) { if(item==p->leftChild->data) return p; else p=p->leftChild; } else return NULL; } else { if(p->rightChild!=NULL) { if(item==p->rightChild->data) return p; else p=p->rightChild; } else return NULL; } } } } ///////////////////////////////////////////////////////////////////////下面这个函数是给出结点的地址,要寻找它的双亲结点,其方法与上一个函数类似。可以利用函数重载的方法,而没有必要另起一个函数名。 template<class T> BTNode<T>*findParentNodeInSortTree(BTNode<T>*root,BTNode<T>*node,bool recursionFlag) { if(recursionFlag) { if(root==NULL||root==node) return NULL; else if(node->data<root->data) { if(root->leftChild!=NULL) { if(root->leftChild==node) return root; else return findParentNodeInSortTree(root->leftChild,node,true); } else return NULL; } else { if(root->rightChild!=NULL) { if(node==root->rightChild) return root; else return findParentNodeInSortTree(root->rightChild,node,true); } else return NULL; } } else //采用非递归方法。 { BTNode<T>*p=root; while(1) { if(p==NULL||root==node) return NULL; else if(node->data<p->data) { if(p->leftChild!=NULL) { if(p->leftChild==node) return p; else p=p->leftChild; } else return NULL; //其实这个分类情况跟if(p==NULL||root==node)重复了。但是提前一步返回也未尝不可。 } else { if(p->rightChild!=NULL) { if(p->rightChild==node) return p; else p=p->rightChild; } else return NULL; } } } } /////////////////////////下面这个函数用于删除二叉查找树中的结点。 template<class T> void deleteNodeInSortTree(BTNode<T>*&root,BTNode<T>*targetNode,BTNode<T>*targetParentNode) { BTNode<T>*r,*s; int flag=0; if(targetNode->leftChild==NULL) { if(targetNode==root) root=targetNode->rightChild; else { r=targetNode->rightChild; flag=1; } } else if(targetNode->rightChild==NULL) { if(targetNode==root) root=targetNode->leftChild; else { r=targetNode->leftChild; flag=1; } } else { ////////////////////////首先要找到targetNode的右子树中最小的那个节点。 s=targetNode; r=s->rightChild; while(r->leftChild!=NULL) { s=r; r=r->leftChild; } r->leftChild=targetNode->leftChild;//千万别忘了这句。 /////////////////至此,就找到了targetNode的右子树中最小的那个节点。 if(s!=targetNode) { s->leftChild=r->rightChild; r->rightChild=targetNode->rightChild; } /////////////////////////////下面这几句特别重要千万别忘了。 if(targetNode==root) root=r; else flag=1; } if(flag==1) { if(targetParentNode->leftChild==targetNode) targetParentNode->leftChild=r; else targetParentNode->rightChild=r; delete targetNode; targetNode=NULL; } } //////////////////////////////////////////下面这个函数用于销毁一棵二叉树。 template<class T> void destroyBinaryTree(BTNode<T>*&root,bool recursionFlag)//要销毁二叉树的话,显然是用后序遍历的方法最合适。 { if(recursionFlag) { if(root==NULL) return; else { destroyBinaryTree(root->leftChild,true);//这里之所以不用加if(root->leftChild!=NULL)这个条件是因为前面的if(root==NULL)其实就已经把这种情况考虑进去了。 destroyBinaryTree(root->rightChild,true); delete root; root=NULL; } } else { /////////////////////////////很简单,也用后序遍历的非递归方法就行嘛。 if(root==NULL) return; else { stack<BTNode<T>*>nodeStack; stack<int>flagStack; int flag; BTNode<T>*p=root; do { while(p!=NULL) { nodeStack.push(p); flagStack.push(1); p=p->leftChild; } p=nodeStack.top(); nodeStack.pop(); flag=flagStack.top(); flagStack.pop(); //////////////////// if(flag==1) { nodeStack.push(p); flagStack.push(2); p=p->rightChild;//这句很关键,如果忘了的话就遍历不到右子树。 } else { delete p; p=NULL; } } while (!nodeStack.empty()||p!=NULL); } } } /////////////////////////////////////////////////////////// template<class T> void deleteNodeInSortTree(BTNode<T>*&root,T item) { BTNode<T>*targetNode=findNodeInSortTree(root,item,true); BTNode<T>*targetParentNode=findParentNodeInSortTree(root,item,true); deleteNodeInSortTree(root,targetNode,targetParentNode); cout<<"删除节点"<<item<<"成功!"<<endl; } //////////////////////////////////////////////////////////////////////下面这个函数用于删除普通二叉树中的节点。必须明确的一点是:对于二叉查找树,可以单独删除一个节点,因为删除后其它点可以按原来的规则进行整合; //但是,对于普通的二叉树,没有通用的整合规则,所以我们在删除普通二叉树的结点时,不可能单独删除它这一个,还必须删除它的左子树和右子树。 //另外,要记得将它的双亲结点的左子树或者右子树置为NULL。 template<class T> void deleteNodeInOrdinaryBinaryTree(BTNode<T>*&root,T item,bool recursionFlag) { //////////////////////////首先要找到该节点。记得分类讨论。 BTNode<T>*node,*parentNode; findNodeAndParentInSortTree(root,item,node,parentNode,recursionFlag); if(node==NULL)//即没有找到。 return; else if(parentNode==NULL)//说明是根节点。因为除了根结点之外,其它所有结点都有双亲结点。 { destroyBinaryTree(root,recursionFlag); root=NULL; } else { if(parentNode->leftChild==node) parentNode->leftChild=NULL; else parentNode->rightChild=NULL; destroyBinaryTree(node,recursionFlag); } } /////////////////////////////////////////////下面这个函数用于判断两个二叉树是否相似。 template<class T> bool isSimiliar(BTNode<T>*root01,BTNode<T>*root02,bool recursionFlag) { if(recursionFlag) { if(root01==NULL&&root02==NULL) return true; else if(root01==NULL||root02==NULL) return false; else { return (isSimiliar(root01->leftChild,root02->leftChild,true))&&(isSimiliar(root01->rightChild,root02->rightChild,true)); } } else //采用非递归的方法该怎么判断呢?利用两个堆栈应该就可以判断吧! { BTNode<T>*p1=root01,*p2=root02; stack<BTNode<T>*>nodeStack1; stack<BTNode<T>*>nodeStack2; do { while(p1!=NULL&&p2!=NULL) { nodeStack1.push(p1); nodeStack2.push(p2); p1=p1->leftChild; p2=p2->leftChild; } /////////////////////////如果只是因为其中的某一个为NULL而跳出,那么肯定是不相似。 if(p1!=NULL||p2!=NULL) return false; p1=nodeStack1.top(); nodeStack1.pop(); p2=nodeStack2.top(); nodeStack2.pop(); ///////////////////////////// p1=p1->rightChild; p2=p2->rightChild; } while ((!nodeStack1.empty()||p1!=NULL)&&(!nodeStack2.empty()||p2!=NULL)); //////////////////////////////////////////如果中断循环是因为有一个stack为空或者只是一个指针为NULL的话,就要返回false. //if(nodeStack1.empty()||nodeStack2.empty()) //首先要判断导致循环中断的条件是什么。仔细想一下的话就会发现这个判断是多余的。因为它会中断循环至少有一个stack为空而且p为NULL。 if(!nodeStack1.empty()||!nodeStack2.empty()) return false; //if(p1==NULL||p2==NULL) //同样地,这个判断条件也是多余的。 if(p1!=NULL||p2!=NULL) return false; /////////////////////////////////////如果到这里还没有返回的话,那就说明是相似了。 return true; } } ////////////////////////下面这个函数用于判断两个二叉树是否相等。 template<class T> bool isEqual(BTNode<T>*root01,BTNode<T>*root02,bool recursionFlag) { if(recursionFlag) { if(root01==NULL&&root02==NULL) return true; else if(root01==NULL||root02==NULL)//这里其实既可以写成现在这样,也可以写成else if(root01!=NULL||root02!=NULL) return false; else { if(root01->data==root02->data) //return true; //此时不是return true,而是应该继续往下比较。 return isEqual(root01->leftChild,root02->leftChild,true)&&isEqual(root01->rightChild,root02->rightChild,true); else return false; } } else //采用非递归算法该怎么比较呢? { stack<BTNode<T>*>nodeStack1,nodeStack2; BTNode<T>*p1=root01,*p2=root02; do { while(p1!=NULL&&p2!=NULL) { nodeStack1.push(p1); nodeStack2.push(p2); p1=p1->leftChild; p2=p2->leftChild; } /////////////////////// if(p1!=NULL||p2!=NULL) return false; //////////////////////// p1=nodeStack1.top(); nodeStack1.pop(); p2=nodeStack2.top(); nodeStack2.pop(); //////////////////////// if(p1->data!=p2->data) return false; else { p1=p1->rightChild; p2=p2->rightChild; } } while ((!nodeStack1.empty()||p1!=NULL)&&(!nodeStack2.empty()||p2!=NULL)); //////////////////////////////////// if(!nodeStack1.empty()||!nodeStack2.empty()) return false; if(p1!=NULL||p2!=NULL) return false; ///////////////////////////////////如果到这里还没有return false的话,就要return true了。 return true; } } ///////////////////////////////////////////////////////////下面这个函数用于获取二叉树的尝试。 template<typename T> int getBinaryTreeDepth(BTNode<T>*root,bool recursionFlag) { if(recursionFlag) { if(root==NULL) return 0; else //return 1+getBinaryTreeDepth(root->leftChild)+getBinaryTreeDepth(root->rightChild);//1就是当前不为NULL的节点。 { int maxDepth,leftDepth,rightDepth; leftDepth=getBinaryTreeDepth(root->leftChild,true); rightDepth=getBinaryTreeDepth(root->rightChild,true); maxDepth=leftDepth>rightDepth?leftDepth:rightDepth; return 1+maxDepth; } } else //此处采用类似中序遍历的方法。这里是用堆栈实现的,那么能否利用队列实现呢? { if(root==NULL) return 0; BTNode<T>*nodeStack[100],*p=root; int depthStack[100]; int currentDepth=1,maxDepth=0,top=-1; do { while(p!=NULL) { nodeStack[++top]=p; depthStack[top]=currentDepth; p=p->leftChild; ++currentDepth; } p=nodeStack[top]; currentDepth=depthStack[top--]; if(p->leftChild==NULL&&p->rightChild==NULL) { if(currentDepth>maxDepth) maxDepth=currentDepth; } p=p->rightChild; ++currentDepth; } while (top>=0||p!=NULL); return maxDepth; //下面这个是利用了STL,经过测试发现正确。 /* if(root==NULL) return 0; //空树的深度为0. stack<BTNode<T>*>nodeStack; stack<int>depthStack; BTNode<T>*p=root; int currentDepth=1,maxDepth=0; do { while(p!=NULL) { nodeStack.push(p); depthStack.push(currentDepth); ++currentDepth; p=p->leftChild; } p=nodeStack.top(); nodeStack.pop(); currentDepth=depthStack.top(); depthStack.pop(); if(p->leftChild==NULL&&p->rightChild==NULL) { if(currentDepth>maxDepth) maxDepth=currentDepth; } p=p->rightChild; ++currentDepth;//只要是进入下一层,它的深度就要加1. } while (!nodeStack.empty()||p!=NULL); return maxDepth; */ } } /////////////////////////////////////////////////////////下面这个函数用于求某个值在二叉树中所处的层次。 template<class T> int getLayerNum(BTNode<T>*root,T item,bool recursionFlag,int methodNum=1) { if(recursionFlag)//这个递归算法其实是比非递归算法更难想到。 { if(root==NULL) return 0; else if(root->data==item) return 1; int layerNum; layerNum=getLayerNum(root->leftChild,item,true); if(layerNum>0) return (layerNum+1);//因为要加上根结点嘛。 /////////////////////////////////如果到这里还没有返回的话,就要遍历右子树了。 layerNum=getLayerNum(root->rightChild,item,true); if(layerNum>0) return (layerNum+1); //////////////////////////如果遍历完右子树还没有返回,就表示还没有找到,这样就要返回0了。 return 0; } else //虽然都是求深度,但是这里有3种方法,分别对应前序、中序、后序遍历方法。 { if(methodNum==1) { if(root==NULL) return 0; stack<BTNode<T>*>nodeStack; stack<int>depthStack; int currentDepth=1; BTNode<T>*p=root; do { while(p!=NULL) { nodeStack.push(p); depthStack.push(currentDepth); p=p->leftChild; //只要是向下走,深度就要加1. currentDepth++; } p=nodeStack.top(); nodeStack.pop(); currentDepth=depthStack.top(); depthStack.pop(); if(p->data==item) return currentDepth; else //这个else其实可写可不写。 { p=p->rightChild; ++currentDepth; } } while (!nodeStack.empty()||p!=NULL); return 0;//如果遍历所有结点还是没有找到的话,那么肯定是二叉树中不存在这样的结点,所以返回0. } else if(methodNum==2)//前面是采用类似中序遍历的方法来求深度,在这个过程中就可以求出节点所在的层次。当然也可以采用类似前序遍历的方法来求深度。 { if(root==NULL) return 0; stack<BTNode<T>*>nodeStack; stack<int>depthStack; int currentDepth=1; BTNode<T>*p=root; do { while(p!=NULL) { if(p->data==item) return currentDepth; //else nodeStack.push(p); depthStack.push(currentDepth); p=p->leftChild; ++currentDepth;//只要是向下,深度就要加1. } p=nodeStack.top(); nodeStack.pop(); currentDepth=depthStack.top(); depthStack.pop(); p=p->rightChild;//只要是向下,深度就要加1. ++currentDepth; } while (!nodeStack.empty()||p!=NULL); /////////////////////如果遍历所有节点还没有找到的话,就要返回0. return 0; } else //用后序遍历的算法时由于currentDepth==top+2==stack.size()+1,所以就不必再加一个depthStack了。 { if(root==NULL) return 0; stack<BTNode<T>*>nodeStack; stack<int>flagStack; BTNode<T>*p=root; int flag; do { while(p!=NULL) { nodeStack.push(p); flagStack.push(1); p=p->leftChild; } p=nodeStack.top(); nodeStack.pop(); flag=flagStack.top(); flagStack.pop(); if(flag==1) { nodeStack.push(p); flagStack.push(2); p=p->rightChild; } else { if(p->data==item) return (nodeStack.size()+1); //else p=NULL; } } while (!nodeStack.empty()||p!=NULL); ////////////////////////如果遍历完还没找到,就返回0. return 0; } } } ///////////////////////////////////////////////////////////下面这个函数用于得到叶节点的数目。 template<class T> int getLeafNodeCount(BTNode<T>*root,bool recursionFlag) { if(recursionFlag) { if(root==NULL) return 0; if(root->leftChild==NULL&&root->rightChild==NULL) //这种分类方法极化。如果是按root->left!=NULL及root->rightChild!=NULL来分的话,情况就会很多很乱。 return 1; else return getLeafNodeCount(root->leftChild,true)+getLeafNodeCount(root->rightChild,true); } else //对于非递归方法,思路很简单,就是进行遍历,对每个结点进行判断。此处采用按层次遍历的非递归算法。 { if(root==NULL) return 0; BTNode<T>*p; deque<BTNode<T>*>nodeQueue; nodeQueue.push_back(root); int leafCount=0; while(!nodeQueue.empty()) { p=nodeQueue.front(); nodeQueue.pop_front(); if(p->leftChild==NULL&&p->rightChild==NULL) ++leafCount; if(p->leftChild!=NULL) nodeQueue.push_back(p->leftChild); if(p->rightChild!=NULL) nodeQueue.push_back(p->rightChild); } return leafCount; } } /////////////////////////////////////////////////////////////下面这个函数用于求二叉树中结点的个数,其实好简单,就是遍历计数就行。 template<typename T> int getNodeCount(BTNode<T>*root,bool recursionFlag,int methodNum=1) { if(recursionFlag) //前序遍历。 { static int nodeCount=0; if(root==NULL) return 0; ++nodeCount; getNodeCount(root->leftChild,true); getNodeCount(root->rightChild,true); return nodeCount; } else if(methodNum==1)//前序 { if(root==NULL) return 0; int nodeCount=0; BTNode<T>*nodeStack[100],*p=root; int top=-1; do { while(p!=NULL) { ++nodeCount; nodeStack[++top]=p; p=p->leftChild; } p=nodeStack[top--]; p=p->rightChild; } while (top>=0||p!=NULL); return nodeCount; } else if(methodNum==2)//中序遍历。 { if(root==NULL) return 0; BTNode<T>*p=root; stack<BTNode<T>*>nodeStack; long nodeCount=0; do { while(p!=NULL) { nodeStack.push(p); p=p->leftChild; } p=nodeStack.top(); nodeStack.pop(); ++nodeCount; p=p->rightChild; } while (!nodeStack.empty()||p!=NULL); return nodeCount; } else if(methodNum==3)//后序 { if(root==NULL) return 0; BTNode<T>*p=root; stack<BTNode<T>*>nodeStack; stack<bool>flagStack; //其实用bool就行了。bool型变量只占一个字节。 bool flag; long nodeCount=0; do { while(p!=NULL) { nodeStack.push(p); flagStack.push(false); p=p->leftChild; } p=nodeStack.top(); nodeStack.pop(); flag=flagStack.top(); flagStack.pop(); if(flag==false) { nodeStack.push(p); flagStack.push(true); p=p->rightChild; } else { ++nodeCount; p=NULL; //这一步很关键,没有它的话就会陷入死循环。 } } while (!nodeStack.empty()||p!=NULL); return nodeCount; } else //层次遍历。 { if(root==NULL) return 0; long nodeCount=0; BTNode<T>*p; deque<BTNode<T>*>nodeQueue; nodeQueue.push_back(root); while(!nodeQueue.empty()) { p=nodeQueue.front(); nodeQueue.pop_front(); ++nodeCount; if(p->leftChild!=NULL) nodeQueue.push_back(p->leftChild); if(p->rightChild!=NULL) nodeQueue.push_back(p->rightChild); } return nodeCount; } } /////////////////////////////////////////////////////////下面这个函数用于求第k层的结点数。 template<class T> int getNodeCountOfSpecificLayer(BTNode<T>*root,int k,bool recursionFlag,short methodNum=1) { static int depth=getBinaryTreeDepth(root,true);//首先要求出树的深度,k不能超过当前树的深度。 if(recursionFlag) { if(root==NULL||k<=0||k>depth) return 0; if(k==1) return 1; int leftNodeCount=getNodeCountOfSpecificLayer(root->leftChild,k-1,true); //注意:对于根结点为第k层的结点,对于根结点的子结点构成的二叉树来说,它就处于第k-1层,因而可以一直这样递归下去。 int rightNodeCount=getNodeCountOfSpecificLayer(root->rightChild,k-1,true); return leftNodeCount+rightNodeCount; } else if(methodNum==1) //知道了怎么求节点所在的层次的话,这个非递归方法就不在话下了。这里把种方法都写一下。首先写后序的方法。 { if(root==NULL||k<=0||k>depth) return 0; BTNode<T>*nodeStack[100],*p=root; bool flagStack[100]; //用bool比较节省内存。 bool flag; int top=-1; int nodeCount=0; do { while(p!=NULL) { nodeStack[++top]=p; flagStack[top]=false; p=p->leftChild; } p=nodeStack[top]; flag=flagStack[top--]; if(flag==false) { nodeStack[++top]=p; flagStack[top]=true; p=p->rightChild;//因为是后序,所以先不遍历根结点,而是转到右结点。 } else { if(top+2==k) //即如果当前结点就在第k 层。 nodeCount++; p=NULL; //这一步很关键。 } } while (top>=0||p!=NULL); return nodeCount; } else if(methodNum==2) //中序方法。 { if(root==NULL||k<=0||k>depth) return 0; stack<BTNode<T>*>nodeStack; stack<int>depthStack; BTNode<T>*p=root; int currentDepth=1,nodeCount=0; do { while(p!=NULL) { nodeStack.push(p); depthStack.push(currentDepth); p=p->leftChild; ++currentDepth; } p=nodeStack.top(); nodeStack.pop(); currentDepth=depthStack.top(); depthStack.pop(); if(currentDepth==k) ++nodeCount; p=p->rightChild; currentDepth++; } while (!nodeStack.empty()||p!=NULL); return nodeCount; } else //前序方法。 { if(root==NULL||k<=0||k>depth) return 0; BTNode<T>*p=root; stack<BTNode<T>*>nodeStack; stack<int>depthStack; int currentDepth=1; long nodeCount=0; do { while(p!=NULL) { nodeStack.push(p); depthStack.push(currentDepth); if(currentDepth==k) ++nodeCount; p=p->leftChild; ++currentDepth; } p=nodeStack.top(); nodeStack.pop(); currentDepth=depthStack.top(); depthStack.pop(); p=p->rightChild; ++currentDepth;//只要向下,深度就要加1. } while (!nodeStack.empty()||p!=NULL); return nodeCount; } } //////////////////////////////////////////////////////////////////////下面这个函数,用于交换其所有的左右结点。 template<class T> void exchangeLeftAndRightChild(BTNode<T>*&root,bool recursionFlag,int methodNum=1) { if(recursionFlag) { if(methodNum==1) { if(root==NULL) return; //事实证明这个交换的部分可以放在exchangeLeftAndRightChild(root->leftChild,true)及exchangeLeftAndRightChild(root->rightChild,true)的前面,中间和后面。这其实就是一个前序、中序、后序遍历的问题。 BTNode<T>*temp=root->leftChild; root->leftChild=root->rightChild; root->rightChild=temp; //////////////// exchangeLeftAndRightChild(root->leftChild,true); exchangeLeftAndRightChild(root->rightChild,true); } else if(methodNum==2) { if(root==NULL) return; exchangeLeftAndRightChild(root->leftChild,true); //事实证明这个交换的部分可以放在exchangeLeftAndRightChild(root->leftChild,true)及exchangeLeftAndRightChild(root->rightChild,true)的前面,中间和后面。这其实就是一个前序、中序、后序遍历的问题。 BTNode<T>*temp=root->leftChild; root->leftChild=root->rightChild; root->rightChild=temp; //////////////// exchangeLeftAndRightChild(root->rightChild,true); } else { if(root==NULL) return; exchangeLeftAndRightChild(root->leftChild,true); exchangeLeftAndRightChild(root->rightChild,true); //事实证明这个交换的部分可以放在exchangeLeftAndRightChild(root->leftChild,true)及exchangeLeftAndRightChild(root->rightChild,true)的前面,中间和后面。这其实就是一个前序、中序、后序遍历的问题。 BTNode<T>*temp=root->leftChild; root->leftChild=root->rightChild; root->rightChild=temp; } } else//该操作采用按层次遍历方法比较合适。遍历过程中访问一个结点时,就将该结点的左、右子树的位置进行交换。当然,其实我们也可以遍历每个结点,交换其左右子树即可。所以非递归算法其实也有三种,只是此处采用层遍历会快一点。 { if(root==NULL) return; deque<BTNode<T>*>nodeQueue; BTNode<T>*p,*temp; nodeQueue.push_back(root); while(!nodeQueue.empty()) { p=nodeQueue.front(); nodeQueue.pop_front(); temp=p->leftChild; p->leftChild=p->rightChild; p->rightChild=temp; if(p->leftChild!=NULL) nodeQueue.push_back(p->leftChild); if(p->rightChild!=NULL) nodeQueue.push_back(p->rightChild); } } } ////////////////////////////////////////////////////////////下面这个函数用于复制二叉树。 template<class T> BTNode<T>*copyBinayTree(BTNode<T>*root,bool recursionFlag) { if(recursionFlag) //这里只能采用前序遍历的方式来建立。 { if(root==NULL) return NULL; BTNode<T>*p=new BTNode<T>(); p->data=root->data; p->leftChild=copyBinayTree(root->leftChild,true); p->rightChild=copyBinayTree(root->rightChild,true); return p; } else //这里采用的是类似前序遍历的方式来复制二叉树,当然也可以采用中序遍历的方式,甚至是后序遍历的方式。 { if(root==NULL) return NULL; BTNode<T>*p=root,*copyRoot,*p2; p2=new BTNode<T>(); copyRoot=p2; stack<BTNode<T>*>nodeStack,nodeStack2; do { while(p!=NULL) { p2->data=p->data; nodeStack.push(p); nodeStack2.push(p2); ////////////// p=p->leftChild; if(p!=NULL) p2->leftChild=new BTNode<T>(); else p2->leftChild=NULL; p2=p2->leftChild; } /////////////////// p=nodeStack.top(); nodeStack.pop(); p2=nodeStack2.top(); nodeStack2.pop(); p=p->rightChild; if(p!=NULL)//注意:此时p已经是原来的p的右孩子。 p2->rightChild=new BTNode<T>(); else p2->rightChild=NULL; p2=p2->rightChild;//当时自己因为忘了这一步而出错。 } while (!nodeStack.empty()||p!=NULL); return copyRoot;//当时犯了一个好低级的错误,就是忘记设置返回值了。 } } /////////////////////////////////////////////////下面这个函数用于判断一棵二叉树是否为完全二叉树。关键是要弄清楚完全二叉树的定义:在一棵二叉树中,只有最下面两层的度可以小于2,并且最下面一层的结点都依次排列在该层最左边的位置上。 template<class T> bool isCompleteBinaryTree(BTNode<T>*root,bool recursionFlag,short methodNum=1) { if(recursionFlag)// 我们可以采用类似前序遍历的方法。 { if(root==NULL) return false; static bool mustHaveNoChild=false;//如果不想用static的话,就要将这个标志作为函数参数向下传递。 BTNode<T>*p; if(mustHaveNoChild) { if(root->leftChild!=NULL||root->rightChild!=NULL) return false; } else { if(root->leftChild!=NULL&&root!=NULL) { return isCompleteBinaryTree(root->leftChild,true)&&isCompleteBinaryTree(root->rightChild,true); } else if(root->leftChild!=NULL&root->rightChild==NULL) { mustHaveNoChild=true; return isCompleteBinaryTree(root->rightChild,true); } else if(root->leftChild==NULL&&root->rightChild!=NULL) return false; else //这个好像是多余的吧,在递归中不会出现这种情况吧? { mustHaveNoChild=true; } } } else if(methodNum==1) { if(root==NULL) return false; BTNode<T>*p; queue<BTNode<T>*>nodeQueue;//用queue其实比deque更好。记住最小权限准则。 nodeQueue.push(root); bool mustHaveNoChild=false; while(!nodeQueue.empty()) { p=nodeQueue.front(); nodeQueue.pop(); if(mustHaveNoChild) { if(p->leftChild!=NULL||p->rightChild!=NULL) return false; } else { if(p->leftChild!=NULL&&p->rightChild!=NULL) { nodeQueue.push(p->leftChild); nodeQueue.push(p->rightChild);//这样就保证是从左到右搜索。 } else if(p->leftChild!=NULL&&p->rightChild==NULL) { mustHaveNoChild=true; nodeQueue.push(p->leftChild);//别忘了这一步。 } else if(p->leftChild==NULL&&p->rightChild!=NULL) return false; else mustHaveNoChild=true;//左右子树都为空的话,那么mustHaveNoChild当然就要为true了。但是也可能是最后一层的叶结点啊,如果是叶结点的话,不能这样吧?它是把叶结点的情况排除了吗?因为它是按层次遍历,到了叶结点这一层的话,所有的结点都不会有子树,所以就不要紧了。这就是他要采用按层次遍历的真实原因! } } /////////////////////////////////如果一直到结束循环都还没返回的话,就说明它是完全二叉树了。 return true; } else if(methodNum==2)//其实非遍历的方法根本不止这一种,不用层次遍历的话,用前序遍历也是能搞定的。事实证明,用前序的方法是没法验证一棵树是否为完全二叉树的,至少用上面的那种思想不行,因为前序方法会有回退,即从更低层回到更上层,而mustHaveNoChild其实是针对同一层及其下层而言的,对于它的上层并没有约束力。 { if(root==NULL) return false; BTNode<T>*p=root; stack<BTNode<T>*>nodeStack; bool mushHaveNoChild=false; int depth=getBinaryTreeDepth(root,true); stack<int>depthStack; int currentDepth=1; int lastNodeDepth=1;//初始值为1. do { while(p!=NULL) { nodeStack.push(p); depthStack.push(currentDepth); p=p->leftChild; currentDepth++; } p=nodeStack.top(); nodeStack.pop(); currentDepth=depthStack.top(); depthStack.pop(); if(mushHaveNoChild) { if(p->leftChild!=NULL||p->rightChild!=NULL) return false; } else //下面的程序写得还不够简洁。 { if(p->leftChild!=NULL&&p->rightChild!=NULL) { //p=p->rightChild; } else if(p->leftChild!=NULL&&p->rightChild==NULL) { mushHaveNoChild=true; //p=p->rightChild; } else if(p->leftChild==NULL&&p->rightChild!=NULL) return false; else //要如何排除最后一层的叶结点的情况呢?要排除的话,肯定要加辅助变量,比如先求出其深度,然后加一个深度堆栈,如果是在最后一层的话,出现这种情况就不要置mustHaveNoChild为true。 { if(currentDepth>=lastNodeDepth&¤tDepth<depth) //即使是加了一个lastNodeDepth进去,还是得不到正确结果,说明还是有什么地方没考虑周全。 mushHaveNoChild=true; //p=p->rightChild; } //////////////////////// p=p->rightChild; lastNodeDepth=currentDepth++; //currentDepth++; } } while (!nodeStack.empty()||p!=NULL); return true; } } #endif