二叉树的遍历与线索二叉树

中序遍历非递归

  1. // 使用栈结构,23王道书p141
  2. void InOrder(BiTree T)
  3. {
  4. InitStack(S); // 初始化栈
  5. BiTree p = T; // p为遍历指针
  6. while(p || !IsEmpty(S)) // p非空或者栈非空,后者为了保证出栈的合法性
  7. {
  8. if(p) // 一路向左
  9. {
  10. Push(S,p); // 当前节点入栈
  11. p = p->lchild; // 用左孩子更新p
  12. }
  13. else
  14. {
  15. Pop(S,p); // 出栈
  16. visit(p); // 访问出栈节点,弹出后才可以
  17. p = p->rchild; // 用右孩子更新p
  18. }
  19. }
  20. }

先序遍历非递归

  1. // 使用栈结构,23王道书p141
  2. void InOrder(BiTree T)
  3. {
  4. InitStack(S); // 初始化栈
  5. BiTree p = T; // p为遍历指针
  6. while(p || !IsEmpty(S)) // p非空或者栈非空,后者为了保证出栈的合法性
  7. {
  8. if(p) // 一路向左
  9. {
  10. visit(p); // 访问出栈节点
  11. Push(S,p); // 当前节点入栈
  12. p = p->lchild; // 用左孩子更新p
  13. }
  14. else
  15. {
  16. Pop(S,p); // 出栈
  17. p = p->rchild; // 用右孩子更新p
  18. }
  19. }
  20. }

后序遍历非递归

  1. // 使用栈结构,23王道书p157
  2. // 王道视频中说可以改先序为后序(先序互换左右孩子+一个新栈控制出栈的时机),如果考场忘了以下代码
  3. void PostOrder(BiTree T)
  4. {
  5. InitStack(S); // 初始化栈
  6. BiTree p = T; // p为遍历指针
  7. r = NULL; // 增加辅助指针r,指向最近被访问过的节点
  8. while(p || !IsEmpty(S)) // p非空或者栈非空,后者为了保证出栈的合法性
  9. {
  10. if(p) // 一路向左
  11. { Push(S,p); // 当前节点入栈
  12. p = p->lchild; // 用左孩子更新p
  13. }
  14. else
  15. {
  16. GetTop(S,p); // 读取栈顶元素,与先中序的差异,重点记忆理解区别
  17. if(p->rchild&&p->rchild! = r)// 右子树存在且未被访问过
  18. p= p->rchild; // 用右孩子更新p
  19. else
  20. {
  21. Pop(S,p); // 出栈
  22. visit(p); // 访问该节点,弹出后才可以
  23. r = p // 用右孩子更新p
  24. p = NULL; // 节点访问完,重置p,让第一个if不执行
  25. }
  26. }
  27. }
  28. }

计算树的高度

递归

  1. int Btdepth2(BiTree T)
  2. {
  3. int ldep,rdep;// 分别定义左右树高度
  4. if(T == NULL)// 空树为0
  5. return 0;
  6. ldep = Btdepth(T->lchild);// 左子树高度
  7. rdep = Btdepth(T->rchild);// 右子树高度
  8. if(ldep > rdep)
  9. return ldep+1; // 树的高度为子树最大高度加根节点
  10. else
  11. return rdep+1;
  12. }

非递归

  1. // 层次遍历非递归算法,使用一般的队列,统一++k的模式
  2. int Btdepth(BiTree T){
  3. if(!T)
  4. return 0;
  5. int front = -1, rear = -1;// 初始队头队尾-1
  6. int last = 0,level = 0; // last只被rear赋值,与front比较,记录每一层最右侧节点
  7. BiTree Q[MaxSize]; // 队列Q,存储指针元素,空间足够
  8. Q[++rear] = T; // 根节点入队
  9. BiTree p; // 工作指针p
  10. while(front < rear) // 队列非空
  11. p = Q[++front];
  12. if(p->lchild)
  13. Q[++rear] = p->lchild; // 左孩子入队
  14. if(p->rchild)
  15. Q[++rear] = p->rchild; // 右孩子入队
  16. if (front == last) // 处理该层最右侧节点
  17. {
  18. ++level; //层数+1
  19. last = rear; // last改变为下一层
  20. }
  21. }

拓展:该算法可以修改用于验证是否一棵二叉树是满二叉树,利用 2^h-1 =n

  1. // 队列判完全二叉树,算法的关键部分可以嵌套记忆
  2. bool IsComplete(BiTree T){
  3. InitQueue(Q);
  4. if(!T)
  5. reuturn 1; // 空树为满二叉树
  6. EnQueue(Q,T);
  7. while(!IsEmpty)
  8. DeQueue(Q,p);
  9. if(p)
  10. {
  11. EnQueue(Q,p->lchild);
  12. EnQueue(Q,p->rchild);
  13. }
  14. else
  15. {
  16. while(!IsEmpty)
  17. DeQueue(Q,p);
  18. if(p) // 结点非空,则二叉树为非完全二叉树
  19. return 0;
  20. }
  21. }

判断完全二叉树

  1. // 队列判完全二叉树,算法的关键部分可以嵌套记忆
  2. bool IsComplete(BiTree T){
  3. InitQueue(Q);
  4. if(!T)
  5. reuturn 1; // 空树为满二叉树
  6. EnQueue(Q,T);
  7. while(!IsEmpty(Q))
  8. DeQueue(Q,p);
  9. if(p)
  10. {
  11. EnQueue(Q,p->lchild);
  12. EnQueue(Q,p->rchild);
  13. }
  14. else
  15. {
  16. while(!IsEmpty)
  17. DeQueue(Q,p);
  18. if(p) // 结点非空,则二叉树为非完全二叉树
  19. return 0;
  20. }
  21. }

删除树中x为root的子树

  1. // 后序递归删除
  2. void DeleteXTree(BiTree *bt)
  3. {
  4. //先删除左右孩子才能free根节点x
  5. DeleteXTree((*bt)->lchild);//左
  6. DeleteXTree((*bt)->rchild);//右
  7. free((*bt)); //根
  8. }
  9. // 层次遍历+队列,不是删除的节点就入队列,是就删除该节点
  10. // 用层次遍历容易找到某个节点的父节点,要遍历完整二叉树
  11. void Search(BiTree T)
  12. { BiTree Q[];// 二叉树队列
  13. BiTNode *p;
  14. if(T)
  15. {
  16. if(T->data == x)
  17. {
  18. DeleteXTree(&T);
  19. exit(0);
  20. }
  21. InitQueue(Q);
  22. EnQueue(Q,bt);
  23. while(!isEmpty(Q)){// 如果队列不为空
  24. DeQueue(Q,p);
  25. if(p->lchild) // 左子女非空
  26. {
  27. if(p->lchild->data == x) // 符合x的节点,删除
  28. DeleteXTree(&(p->lchild));
  29. p->lchild = NULL; // 左孩子置空,防止野指针
  30. else
  31. EnQueue(Q,p->lchild); // 不符合x的节点,入队
  32. }
  33. if(p->rchild) // 右子女非空
  34. {
  35. if(p->rchild->data == x) // 符合x的节点,删除
  36. DeleteXTree(&(p->rchild));
  37. p->rchild = NULL; // 右孩子置空,防止野指针
  38. else
  39. EnQueue(Q,p->rchild); // 不符合x的节点,入队
  40. }
  41. }
  42. }

任意两节点找公共祖先结点(链式存储的存储结构)

  1. // 方法一:递归来自知乎
  2. BiTNode * Ancestor(BiTNode *ROOT,BiTNode *p,BiTNode*q)
  3. {
  4. if(ROOT== NULL || node1 == NULL || node2 == NULL)
  5. return NULL;
  6. if(node1 == ROOT || node2 == NULL)
  7. return ROOT;
  8. BiTNode * lacs = Ancestor(ROOT->left,p,q);
  9. BiTNode * racs = Ancestor(ROOT->right,p,q);
  10. if(lacs && racs)
  11. return root;
  12. if(lacs == NULL)
  13. return racs;
  14. else
  15. return lacs;
  16. }
  1. // 容易想到是后序遍历,而且容易想到非递归的后序遍历,即利用栈
  2. // 来自csdn,几乎是两遍后序非递归,代码长,但对称度极高,比王道参考书给出的更容易想到
  3. void Ancestor(BiTNode *ROOT,BiTNode *p,BiTNode*q)
  4. {
  5. Stack S1;
  6. Stack S2;
  7. InitStack(S1); // 初始化栈S1
  8. InitStack(S2); // 初始化栈S2
  9. BiTree b1 = T;
  10. BiTree b2 = T;
  11. BiTree r = NULL; // 增加辅助指针r,指向最近被访问过的节点
  12. while(b1 || !IsEmpty(S1)) // p非空或者栈非空,后者为了保证出栈的合法性
  13. {
  14. if(b1) // 一路向左
  15. { Push(S,b1); // 当前节点入栈
  16. b1 = b1->lchild; // 用左孩子更新p
  17. }
  18. else
  19. {
  20. GetTop(S1,b1); // 读取栈顶元素,与先中序的差异,重点记忆理解区别
  21. if(b1->data == p)
  22. break;
  23. if(b1->rchild&&b1->rchild! = r)// 右子树存在且未被访问过
  24. b1= b1->rchild; // 用右孩子更新p
  25. else
  26. {
  27. Pop(S1,b1); // 出栈
  28. r = b1 // 更新r
  29. b1 = NULL; // 节点访问完,重置p,让第一个if不执行
  30. }
  31. }
  32. }
  33. // 以下while以上面完全对称
  34. while(b2 || !IsEmpty(S2)) // b2非空或者栈非空,后者为了保证出栈的合法性
  35. {
  36. if(b2) // 一路向左
  37. { Push(S2,b2); // 当前节点入栈
  38. b2 = b2->lchild; // 用左孩子更新b2
  39. }
  40. else
  41. {
  42. GetTop(S2,b2); // 读取栈顶元素,与先中序的差异,重点记忆理解区别
  43. /*****/
  44. if(b2->data == q)
  45. break;
  46. /*****/
  47. if(b2->rchild&&b2->rchild! = r)// 右子树存在且未被访问过
  48. b2= b2->rchild; // 用右孩子更新b2
  49. else
  50. {
  51. Pop(S2,b2); // 出栈
  52. r = b2 // 更新r
  53. b2 = NULL; // 节点访问完,重置b2,让第一个if不执行
  54. }
  55. }
  56. }
  57. while( !IsEmpty(S1)&&!IsEmpty(S2))
  58. {
  59. BiTNode *m1,*m2;
  60. Pop(S1,m1);
  61. Pop(S2,m2);
  62. if(m1->data == m2->data)
  63. return p;
  64. }
  65. return NULL;
  66. }

任意两节点找最近公共祖先结点(顺序存储的存储结构)

按照完全二叉树的顺序,空结点用0元素填充,完全二叉树的性质,任意节点,其左节点为2i,右节点2i-1

tips:可以采取栈的方式记录人一个节点的查询路径,查询任意一个节点的根节点的index

  1. // 顺序存储二叉树的最近公共祖先
  2. // 自写参考
  3. int ComAncestor(SqTree T,int i,int j)
  4. { int p,q; // 取栈顶元素
  5. InitStack(S1);
  6. InitStack(S2);
  7. if(T.data[i])
  8. Push(S1,i]) // 元素入栈
  9. if(T.data[j])
  10. Push(S1,j) // 元素入栈
  11. while(T.data[i]&&i>=1)
  12. Push(S1,i/2]) // 父节点入栈,只到根节点入栈
  13. while(T.data[j]&&j>=1)
  14. Push(S2,j/2]) // 父节点入栈,只到根节点入栈
  15. // 逆向出栈,index大的先出栈
  16. while(!IsEmpty(S1)&&!IsEmpty(S2))
  17. {
  18. p = GetTop(S1);
  19. q = GetTop(S2);
  20. if (p < q)
  21. q = GetTop(S2);
  22. else if(p > q)
  23. p = GetTop(S1);
  24. else if(p == q)
  25. return p;
  26. }
  27. }
  1. // 标准答案,思路差不多,就是节点的index大的减半,不过最后返回元素
  2. ElemType Comm_Ancestor(SqTree T,int i,int j)
  3. {
  4. //本算法目的判断顺序存储的二叉树的最近公共节点
  5. if(T[i]!="#"&&T[j]!="#")
  6. {
  7. while(i != j)
  8. {
  9. if(i > j)
  10. i = i/2;
  11. else
  12. j = j/2;
  13. }
  14. return T[i];
  15. }
  16. }