二叉树的遍历与线索二叉树
中序遍历非递归
// 使用栈结构,23王道书p141void InOrder(BiTree T){InitStack(S); // 初始化栈BiTree p = T; // p为遍历指针while(p || !IsEmpty(S)) // p非空或者栈非空,后者为了保证出栈的合法性{if(p) // 一路向左{Push(S,p); // 当前节点入栈p = p->lchild; // 用左孩子更新p}else{Pop(S,p); // 出栈visit(p); // 访问出栈节点,弹出后才可以p = p->rchild; // 用右孩子更新p}}}
先序遍历非递归
// 使用栈结构,23王道书p141void InOrder(BiTree T){InitStack(S); // 初始化栈BiTree p = T; // p为遍历指针while(p || !IsEmpty(S)) // p非空或者栈非空,后者为了保证出栈的合法性{if(p) // 一路向左{visit(p); // 访问出栈节点Push(S,p); // 当前节点入栈p = p->lchild; // 用左孩子更新p}else{Pop(S,p); // 出栈p = p->rchild; // 用右孩子更新p}}}
后序遍历非递归
// 使用栈结构,23王道书p157// 王道视频中说可以改先序为后序(先序互换左右孩子+一个新栈控制出栈的时机),如果考场忘了以下代码void PostOrder(BiTree T){InitStack(S); // 初始化栈BiTree p = T; // p为遍历指针r = NULL; // 增加辅助指针r,指向最近被访问过的节点while(p || !IsEmpty(S)) // p非空或者栈非空,后者为了保证出栈的合法性{if(p) // 一路向左{ Push(S,p); // 当前节点入栈p = p->lchild; // 用左孩子更新p}else{GetTop(S,p); // 读取栈顶元素,与先中序的差异,重点记忆理解区别if(p->rchild&&p->rchild! = r)// 右子树存在且未被访问过p= p->rchild; // 用右孩子更新pelse{Pop(S,p); // 出栈visit(p); // 访问该节点,弹出后才可以r = p // 用右孩子更新pp = NULL; // 节点访问完,重置p,让第一个if不执行}}}}
计算树的高度
递归
int Btdepth2(BiTree T){int ldep,rdep;// 分别定义左右树高度if(T == NULL)// 空树为0return 0;ldep = Btdepth(T->lchild);// 左子树高度rdep = Btdepth(T->rchild);// 右子树高度if(ldep > rdep)return ldep+1; // 树的高度为子树最大高度加根节点elsereturn rdep+1;}
非递归
// 层次遍历非递归算法,使用一般的队列,统一++k的模式int Btdepth(BiTree T){if(!T)return 0;int front = -1, rear = -1;// 初始队头队尾-1int last = 0,level = 0; // last只被rear赋值,与front比较,记录每一层最右侧节点BiTree Q[MaxSize]; // 队列Q,存储指针元素,空间足够Q[++rear] = T; // 根节点入队BiTree p; // 工作指针pwhile(front < rear) // 队列非空p = Q[++front];if(p->lchild)Q[++rear] = p->lchild; // 左孩子入队if(p->rchild)Q[++rear] = p->rchild; // 右孩子入队if (front == last) // 处理该层最右侧节点{++level; //层数+1last = rear; // last改变为下一层}}
拓展:该算法可以修改用于验证是否一棵二叉树是满二叉树,利用 2^h-1 =n
// 队列判完全二叉树,算法的关键部分可以嵌套记忆bool IsComplete(BiTree T){InitQueue(Q);if(!T)reuturn 1; // 空树为满二叉树EnQueue(Q,T);while(!IsEmpty)DeQueue(Q,p);if(p){EnQueue(Q,p->lchild);EnQueue(Q,p->rchild);}else{while(!IsEmpty)DeQueue(Q,p);if(p) // 结点非空,则二叉树为非完全二叉树return 0;}}
判断完全二叉树
// 队列判完全二叉树,算法的关键部分可以嵌套记忆bool IsComplete(BiTree T){InitQueue(Q);if(!T)reuturn 1; // 空树为满二叉树EnQueue(Q,T);while(!IsEmpty(Q))DeQueue(Q,p);if(p){EnQueue(Q,p->lchild);EnQueue(Q,p->rchild);}else{while(!IsEmpty)DeQueue(Q,p);if(p) // 结点非空,则二叉树为非完全二叉树return 0;}}
删除树中x为root的子树
// 后序递归删除void DeleteXTree(BiTree *bt){//先删除左右孩子才能free根节点xDeleteXTree((*bt)->lchild);//左DeleteXTree((*bt)->rchild);//右free((*bt)); //根}// 层次遍历+队列,不是删除的节点就入队列,是就删除该节点// 用层次遍历容易找到某个节点的父节点,要遍历完整二叉树void Search(BiTree T){ BiTree Q[];// 二叉树队列BiTNode *p;if(T){if(T->data == x){DeleteXTree(&T);exit(0);}InitQueue(Q);EnQueue(Q,bt);while(!isEmpty(Q)){// 如果队列不为空DeQueue(Q,p);if(p->lchild) // 左子女非空{if(p->lchild->data == x) // 符合x的节点,删除DeleteXTree(&(p->lchild));p->lchild = NULL; // 左孩子置空,防止野指针elseEnQueue(Q,p->lchild); // 不符合x的节点,入队}if(p->rchild) // 右子女非空{if(p->rchild->data == x) // 符合x的节点,删除DeleteXTree(&(p->rchild));p->rchild = NULL; // 右孩子置空,防止野指针elseEnQueue(Q,p->rchild); // 不符合x的节点,入队}}}
任意两节点找公共祖先结点(链式存储的存储结构)
// 方法一:递归来自知乎BiTNode * Ancestor(BiTNode *ROOT,BiTNode *p,BiTNode*q){if(ROOT== NULL || node1 == NULL || node2 == NULL)return NULL;if(node1 == ROOT || node2 == NULL)return ROOT;BiTNode * lacs = Ancestor(ROOT->left,p,q);BiTNode * racs = Ancestor(ROOT->right,p,q);if(lacs && racs)return root;if(lacs == NULL)return racs;elsereturn lacs;}
// 容易想到是后序遍历,而且容易想到非递归的后序遍历,即利用栈// 来自csdn,几乎是两遍后序非递归,代码长,但对称度极高,比王道参考书给出的更容易想到void Ancestor(BiTNode *ROOT,BiTNode *p,BiTNode*q){Stack S1;Stack S2;InitStack(S1); // 初始化栈S1InitStack(S2); // 初始化栈S2BiTree b1 = T;BiTree b2 = T;BiTree r = NULL; // 增加辅助指针r,指向最近被访问过的节点while(b1 || !IsEmpty(S1)) // p非空或者栈非空,后者为了保证出栈的合法性{if(b1) // 一路向左{ Push(S,b1); // 当前节点入栈b1 = b1->lchild; // 用左孩子更新p}else{GetTop(S1,b1); // 读取栈顶元素,与先中序的差异,重点记忆理解区别if(b1->data == p)break;if(b1->rchild&&b1->rchild! = r)// 右子树存在且未被访问过b1= b1->rchild; // 用右孩子更新pelse{Pop(S1,b1); // 出栈r = b1 // 更新rb1 = NULL; // 节点访问完,重置p,让第一个if不执行}}}// 以下while以上面完全对称while(b2 || !IsEmpty(S2)) // b2非空或者栈非空,后者为了保证出栈的合法性{if(b2) // 一路向左{ Push(S2,b2); // 当前节点入栈b2 = b2->lchild; // 用左孩子更新b2}else{GetTop(S2,b2); // 读取栈顶元素,与先中序的差异,重点记忆理解区别/*****/if(b2->data == q)break;/*****/if(b2->rchild&&b2->rchild! = r)// 右子树存在且未被访问过b2= b2->rchild; // 用右孩子更新b2else{Pop(S2,b2); // 出栈r = b2 // 更新rb2 = NULL; // 节点访问完,重置b2,让第一个if不执行}}}while( !IsEmpty(S1)&&!IsEmpty(S2)){BiTNode *m1,*m2;Pop(S1,m1);Pop(S2,m2);if(m1->data == m2->data)return p;}return NULL;}
任意两节点找最近公共祖先结点(顺序存储的存储结构)
按照完全二叉树的顺序,空结点用0元素填充,完全二叉树的性质,任意节点,其左节点为2i,右节点2i-1
tips:可以采取栈的方式记录人一个节点的查询路径,查询任意一个节点的根节点的index
// 顺序存储二叉树的最近公共祖先// 自写参考int ComAncestor(SqTree T,int i,int j){ int p,q; // 取栈顶元素InitStack(S1);InitStack(S2);if(T.data[i])Push(S1,i]) // 元素入栈if(T.data[j])Push(S1,j) // 元素入栈while(T.data[i]&&i>=1)Push(S1,i/2]) // 父节点入栈,只到根节点入栈while(T.data[j]&&j>=1)Push(S2,j/2]) // 父节点入栈,只到根节点入栈// 逆向出栈,index大的先出栈while(!IsEmpty(S1)&&!IsEmpty(S2)){p = GetTop(S1);q = GetTop(S2);if (p < q)q = GetTop(S2);else if(p > q)p = GetTop(S1);else if(p == q)return p;}}
// 标准答案,思路差不多,就是节点的index大的减半,不过最后返回元素ElemType Comm_Ancestor(SqTree T,int i,int j){//本算法目的判断顺序存储的二叉树的最近公共节点if(T[i]!="#"&&T[j]!="#"){while(i != j){if(i > j)i = i/2;elsej = j/2;}return T[i];}}
