利用指针传递参数

最近在给学生讲授数据结构课程的树和二叉树这一章时,调试书的的代码发现几个问题,花了不少时间才解决,感觉自己对指针这玩意的理解还有待深入。

二叉树的遍历有三种,先序、中序和后序,通常是利用递归来进行遍历。如果不利用递归,这时候就要借鉴于堆栈(Stack)这种数据结构。根据树上的定义,我们可以定义以下栈来存储树的节点:

  1. //二叉树节点的定义
  2. typedef struct BiNode {
  3.     TElemType data;
  4.     struct BiNode *lchild, *rchild;     //左右孩子指针
  5. }BiTNode, *BiTree;
  6. #define STACK_INIT_SIZE 100        //存储空间初始分配量
  7. #define STACKINCREMENT  10         //存储空间分配增量
  8. typedef struct {
  9.     BiTNode* base;            //在栈构造之前和销毁后,该指针为NULL
  10.     BiTNode* top;             //栈顶指针
  11.     int stacksize;            //当前已经分配的存储空间,以元素为单位
  12. }SqStack; 

这个地方的关键在于,平时我们入栈的可能是build-in type数据,如int。而这个地方我们入栈的是树的节点。其关键的Push和Pop操作,按照书上的实现如下:

  1. Status Push(SqStack &S,BiTNode* e)
  2. {
  3.     if(S.top - S.base >= S.stacksize)       //栈满,追加存储空间
  4.     {
  5.         S.base = (BiTNode*)realloc(S.base, (S.stacksize + STACKINCREMENT)*sizeof /
  6.                 (BiTNode));
  7.         if(!S.base) exit(OVERFLOW);
  8.         S.top = S.base + S.stacksize;
  9.         S.stacksize += STACKINCREMENT;
  10.     }
  11.     if(e != NULL)
  12.     {
  13.         *S.top = *e;            //e的值(*e)入栈
  14.         S.top++;
  15.     }
  16.     return OK;
  17. } //Push
  18. /*
  19. *       说明:该函数通过参数e将栈顶元素返回
  20. *        但此时的e传递进来时,必须已经赋值
  21. */
  22. Status Pop(SqStack &S, BiTNode* e)
  23. {
  24.     if(S.base == S.top || !e)
  25.     {
  26.         return ERROR;
  27.     }
  28.     --S.top;
  29.     (*e) = *S.top;              //弹出的值赋予给*e
  30.     //e = S.top;                //error.
  31.     return OK;
  32. }

在上面代码中,要注意的是,我们入栈的是节点包含的数据,而不是节点指针本身。在Push实现时,我就犯了以上错误,代码如下:

  1.         S.top = e;            //error!
  2.         S.top++;

结果把栈顶指针给改变了。

但是上述的Pop函数在实际调用过程中也存在着很大问题,见下面代码:

  1. //中序非递归遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
  2. Status InOrderTraverse1(BiTree T, Status(*Visit)(TElemType e))  //方法1(6.2)
  3. {
  4.     SqStack S;
  5.     InitStack(S);
  6.     BiTNode* p = T;
  7.     while(p != NULL || !IsStackEmpty(S))
  8.     {
  9.         while(p != NULL)
  10.         {
  11.             Push(S, p);
  12.             p = p->lchild;
  13.         }
  14.         
  15.         if(!IsStackEmpty(S))
  16.         {
  17.             p = (BiTNode*)malloc(sizeof(BiTNode));
  18.             Pop(S, p);   //Here, the problem is how to delete p?
  19.             if(!Visit(p->data)) 
  20.                 return ERROR;
  21.             
  22.             p = p->rchild;
  23.         }
  24.     }
  25.     DestroyStack(S);
  26.     return OK;
  27. }

见上面我的注释//Here, the problem is how to delete p? 在从栈顶取元素时,我们必须传入指针p,但是此p必须在使用前赋值,否则出错!关键是在何时delete p。想了很久,一直没有找到比较好的解决办法???

最终,采用的是另一种方法,即Pop函数实现如下:

  1. /*
    *       说明:该函数通过函数返回值将栈顶元素返回
    *           
    */
    BiTNode* Pop(SqStack &S)
    {
        if(S.base == S.top)
        {
            return NULL;
        }

        --S.top;

        return S.top;
    }

最终,在遍历函数中调用过程如下:

  1. //中序非递归遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
  2. Status InOrderTraverse1(BiTree T, Status(*Visit)(TElemType e))  //方法1(6.2)
  3. {
  4.     SqStack S;
  5.     InitStack(S);
  6.     BiTNode* p = T;
  7.     while(p != NULL || !IsStackEmpty(S))
  8.     {
  9.         while(p != NULL)
  10.         {
  11.             Push(S, p);
  12.             p = p->lchild;
  13.         }
  14.         
  15.         if(!IsStackEmpty(S))
  16.         {
  17.             p = Pop(S);          //为什么这个调用可以!
  18.             if(!Visit(p->data)) 
  19.                 return ERROR;
  20.             
  21.             p = p->rchild;
  22.         }
  23.     }
  24.     DestroyStack(S);
  25.     return OK;
  26. }

实际上,我们把问题丢给了编译器,因为函数的返回值是在系统Stack中分配的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值