二叉树的常用操作(C++实现)

来源:互联网 发布:淘宝网模特 编辑:程序博客网 时间:2024/05/29 14:37

       声明:本博客为原创博客,未经允许,不得转载!小伙伴们如果是在别的地方看到的话,建议还是来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;elsereturn 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;}elsefindNodeAndParentInSortTree(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;}elsefindNodeAndParentInSortTree(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);elsereturn 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;}elsep=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;elsereturn findParentNodeInSortTree(root->leftChild,item,true);}elsereturn NULL;}else{if(root->rightChild!=NULL){if(item==root->rightChild->data)return root;elsereturn findParentNodeInSortTree(root->rightChild,item,true);}elsereturn 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;elsep=p->leftChild;}else return NULL;}else{if(p->rightChild!=NULL){if(item==p->rightChild->data)return p;elsep=p->rightChild;}elsereturn 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;elsereturn findParentNodeInSortTree(root->leftChild,node,true);}elsereturn NULL;}else{if(root->rightChild!=NULL){if(node==root->rightChild)return root;elsereturn findParentNodeInSortTree(root->rightChild,node,true);}elsereturn 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;elsep=p->leftChild;}elsereturn NULL; //其实这个分类情况跟if(p==NULL||root==node)重复了。但是提前一步返回也未尝不可。}else{if(p->rightChild!=NULL){if(p->rightChild==node)return p;elsep=p->rightChild;}elsereturn 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;elseflag=1;}if(flag==1){if(targetParentNode->leftChild==targetNode)targetParentNode->leftChild=r;elsetargetParentNode->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);elsereturn 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;//elsenodeStack.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;elsereturn 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>();elsep2->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>();elsep2->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;elsemustHaveNoChild=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


 

     

原创粉丝点击