题目要求及思路分析

  • 题目要求:已知在二叉树中, root 为根结点, p 和* q 为二叉树中两个结点,试编写求距离它们最近的共同祖先的算法。 —-《数据结构习题集(C 语言版)》
  • 思路:

    • 显然在这个题目中,递归遍历不适用。同时先中后三种顺序,先序遍历比较合适。
    • 要利用栈的特性来存储访问目标结点的路径,以便于最后查找它的祖先结点。
    • p 和 q 的路径都找到后,我们可以看到根结点在栈底,而目标结点在栈顶,这样的话不利于我们比较两条路径上共同的祖先结点。所以,要将两个目标结点的路径栈逆置,使栈顶元素都为根结点,这样在出栈的时候可以比较两个栈顶元素指向的结点。直到出现第一个不同的结点时,取上一个出栈元素,即为距两目标结点最近的共同祖先结点。

      算法实现

  1. 两种数据类型的结构体定义 ```c /———-二叉树的二叉链结点结构定义———/

    define TElemType char

typedef struct BiTNode{ // 结点结构 TElemType data; // 结点数据 struct BiTNode lchild, rchild; // 左右 孩子指针 } BiTNode, *BiTree;

/———栈的数据结构预定义———/ define MAXSIZE 100 // 存储空间初始分配量

typedef struct{

  1. BiTree data[MAXSIZE];
  2. int top; //用于栈顶指针

}SqStack;

/—————————————-/

  1. 2.
  2. 用到的栈的基本操作函数
  3. ```c
  4. /*-------栈的基本操作函数-------*/
  5. Status Push(SqStack *S, BiTree e){
  6. //插入元素e 为新的栈顶元素
  7. if (S->top = MAXSIZE - 1)return ERROR;
  8. S->top++;
  9. S->data[S->top] = e; //将新插入元素赋值给栈顶空间
  10. return OK;
  11. }
  12. BiTree Pop(SqStack *S){
  13. //若栈不空,则删除S的栈顶元素,用e返回其值,变返回oK;否则返回ERROR
  14. if (S->top == -1)return ERROR;
  15. BiTree e = S->data[S->top]; //将要删除的栈顶元素赋给e
  16. S->top--; //栈顶指针减一
  17. return e;
  18. } //pop
  19. /*-----栈的基本操作函数结束-----*/
  1. 用到的树的基本操作函数 ```c /———树的基本操作的函数———/ //按照二叉树的定义初始化一个空树 Status InitBiTree(BiTree *bt){

    *bt = (BiTree)malloc(sizeof(BiTNode)); if (!bt)return OVERFLOW;

    *bt = NULL;

    return OK; }

//构造二叉链表表示的二叉树T //按先序次序输入二叉树中结点的值(一个字符),空格字符表示空树 Status CreateBiTree(BiTree *T){ TElemType ch;

  1. printf_s("请输入数据:");
  2. scanf_s("%c", &ch);
  3. getchar(); //getchar()用于处理回车占字符的问题
  4. if (ch == '#')
  5. *T = NULL;
  6. else{
  7. *T = (BiTree)malloc(sizeof(BiTNode));
  8. if (!T)return OVERFLOW; // 若内存分配失败,则返回OVERFLOW
  9. (*T)->data = ch; // 生成根结点
  10. CreateBiTree(&((*T)->lchild)); //构建左子树
  11. CreateBiTree(&((*T)->rchild)); //构建右子树
  12. }
  13. return OK;

} /——树的基本操作的函数结束——/

  1. 4.
  2. 利用栈来存储访问目标结点的路径
  3. ```c
  4. /*求距两个子结点最近的共同祖先结点*/
  5. Status FindPath(BiTree root, BiTree target, SqStack *path){
  6. SqStack* s;
  7. s = (SqStack *)malloc(sizeof(SqStack));
  8. BiTree node = root;
  9. while (1){
  10. Push(path, node); //将当前结点入栈
  11. if (node == target)return OK; //若当前结点即为目标结点,则可直接结束
  12. //若左孩子存在,则再看右孩子。若右孩子也存在,将右孩子存入栈s;若右孩子不存在,则直接访问下一个左孩子。
  13. //若左孩子不存在,则访问右孩子。若左右孩子都不存在,则去查看栈s中的栈顶元素所指结点。
  14. //重复操作,直到找到目标结点。这时栈path中存储的元素为访问到目标结点的所有元素。
  15. if (node->lchild){
  16. if (node->rchild){
  17. Push(s, node->rchild);
  18. }
  19. node = node->lchild;
  20. }else if (node->rchild){
  21. node = node->rchild;
  22. }else if (s){
  23. while (path->data[path->top]->rchild != s->data[s->top]){
  24. Pop(path);
  25. }
  26. node = s->data[s->top];
  27. Pop(s);
  28. }else{
  29. break;
  30. }
  31. }
  32. return OK;
  33. }
  1. p、 q 分别调用第 4 步中的函数,将得到的两个路径栈逆置,在逆置后的栈中出栈顶元素同时进行比较,得到公共祖先结点

    1. Status CommentParent(BiTree* parent, BiTree root, BiTree p, BiTree q){
    2. SqStack *path_p, *path_q;
    3. path_p = (SqStack *)malloc(sizeof(SqStack));
    4. path_q = (SqStack *)malloc(sizeof(SqStack));
    5. FindPath(root, p, path_p);
    6. FindPath(root, q, path_q);
    7. SqStack *reverse_p, *reverse_q;
    8. reverse_p = (SqStack *)malloc(sizeof(SqStack));
    9. reverse_q = (SqStack *)malloc(sizeof(SqStack));
    10. while (path_p){
    11. Push(reverse_p, Pop(path_p));
    12. }
    13. while (path_q){
    14. Push(reverse_q, Pop(path_q));
    15. }
    16. while (reverse_p->data[reverse_p->top] == reverse_q->data[reverse_q->top]){
    17. *parent = reverse_p->data[reverse_p->top];
    18. Pop(reverse_p);
    19. Pop(reverse_q);
    20. }
    21. return OK;
    22. }



瓦雀