利用指针传递参数
2008-11-18 14:05
190 查看
最近在给学生讲授数据结构课程的树和二叉树这一章时,调试书的的代码发现几个问题,花了不少时间才解决,感觉自己对指针这玩意的理解还有待深入。
二叉树的遍历有三种,先序、中序和后序,通常是利用递归来进行遍历。如果不利用递归,这时候就要借鉴于堆栈(Stack)这种数据结构。根据树上的定义,我们可以定义以下栈来存储树的节点:
//二叉树节点的定义
typedef struct BiNode {
TElemType data;
struct BiNode *lchild, *rchild; //左右孩子指针
}BiTNode, *BiTree;
#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量
typedef struct {
BiTNode* base; //在栈构造之前和销毁后,该指针为NULL
BiTNode* top; //栈顶指针
int stacksize; //当前已经分配的存储空间,以元素为单位
}SqStack;
这个地方的关键在于,平时我们入栈的可能是build-in type数据,如int。而这个地方我们入栈的是树的节点。其关键的Push和Pop操作,按照书上的实现如下:
Status Push(SqStack &S,BiTNode* e)
{
if(S.top - S.base >= S.stacksize) //栈满,追加存储空间
{
S.base = (BiTNode*)realloc(S.base, (S.stacksize + STACKINCREMENT)*sizeof /
(BiTNode));
if(!S.base) exit(OVERFLOW);
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
if(e != NULL)
{
*S.top = *e; //e的值(*e)入栈
S.top++;
}
return OK;
} //Push
/*
* 说明:该函数通过参数e将栈顶元素返回
* 但此时的e传递进来时,必须已经赋值
*/
Status Pop(SqStack &S, BiTNode* e)
{
if(S.base == S.top || !e)
{
return ERROR;
}
--S.top;
(*e) = *S.top; //弹出的值赋予给*e
//e = S.top; //error.
return OK;
}
在上面代码中,要注意的是,我们入栈的是节点包含的数据,而不是节点指针本身。在Push实现时,我就犯了以上错误,代码如下:
S.top = e; //error!
S.top++;
结果把栈顶指针给改变了。
但是上述的Pop函数在实际调用过程中也存在着很大问题,见下面代码:
//中序非递归遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
Status InOrderTraverse1(BiTree T, Status(*Visit)(TElemType e)) //方法1(6.2)
{
SqStack S;
InitStack(S);
BiTNode* p = T;
while(p != NULL || !IsStackEmpty(S))
{
while(p != NULL)
{
Push(S, p);
p = p->lchild;
}
if(!IsStackEmpty(S))
{
p = (BiTNode*)malloc(sizeof(BiTNode));
Pop(S, p); //Here, the problem is how to delete p?
if(!Visit(p->data))
return ERROR;
p = p->rchild;
}
}
DestroyStack(S);
return OK;
}
见上面我的注释//Here, the problem is how to delete p? 在从栈顶取元素时,我们必须传入指针p,但是此p必须在使用前赋值,否则出错!关键是在何时delete p。想了很久,一直没有找到比较好的解决办法???
最终,采用的是另一种方法,即Pop函数实现如下:
/*
* 说明:该函数通过函数返回值将栈顶元素返回
*
*/
BiTNode* Pop(SqStack &S)
{
if(S.base == S.top)
{
return NULL;
}
--S.top;
return S.top;
}
最终,在遍历函数中调用过程如下:
//中序非递归遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
Status InOrderTraverse1(BiTree T, Status(*Visit)(TElemType e)) //方法1(6.2)
{
SqStack S;
InitStack(S);
BiTNode* p = T;
while(p != NULL || !IsStackEmpty(S))
{
while(p != NULL)
{
Push(S, p);
p = p->lchild;
}
if(!IsStackEmpty(S))
{
p = Pop(S); //为什么这个调用可以!
if(!Visit(p->data))
return ERROR;
p = p->rchild;
}
}
DestroyStack(S);
return OK;
}
实际上,我们把问题丢给了编译器,因为函数的返回值是在系统Stack中分配的。
二叉树的遍历有三种,先序、中序和后序,通常是利用递归来进行遍历。如果不利用递归,这时候就要借鉴于堆栈(Stack)这种数据结构。根据树上的定义,我们可以定义以下栈来存储树的节点:
//二叉树节点的定义
typedef struct BiNode {
TElemType data;
struct BiNode *lchild, *rchild; //左右孩子指针
}BiTNode, *BiTree;
#define STACK_INIT_SIZE 100 //存储空间初始分配量
#define STACKINCREMENT 10 //存储空间分配增量
typedef struct {
BiTNode* base; //在栈构造之前和销毁后,该指针为NULL
BiTNode* top; //栈顶指针
int stacksize; //当前已经分配的存储空间,以元素为单位
}SqStack;
这个地方的关键在于,平时我们入栈的可能是build-in type数据,如int。而这个地方我们入栈的是树的节点。其关键的Push和Pop操作,按照书上的实现如下:
Status Push(SqStack &S,BiTNode* e)
{
if(S.top - S.base >= S.stacksize) //栈满,追加存储空间
{
S.base = (BiTNode*)realloc(S.base, (S.stacksize + STACKINCREMENT)*sizeof /
(BiTNode));
if(!S.base) exit(OVERFLOW);
S.top = S.base + S.stacksize;
S.stacksize += STACKINCREMENT;
}
if(e != NULL)
{
*S.top = *e; //e的值(*e)入栈
S.top++;
}
return OK;
} //Push
/*
* 说明:该函数通过参数e将栈顶元素返回
* 但此时的e传递进来时,必须已经赋值
*/
Status Pop(SqStack &S, BiTNode* e)
{
if(S.base == S.top || !e)
{
return ERROR;
}
--S.top;
(*e) = *S.top; //弹出的值赋予给*e
//e = S.top; //error.
return OK;
}
在上面代码中,要注意的是,我们入栈的是节点包含的数据,而不是节点指针本身。在Push实现时,我就犯了以上错误,代码如下:
S.top = e; //error!
S.top++;
结果把栈顶指针给改变了。
但是上述的Pop函数在实际调用过程中也存在着很大问题,见下面代码:
//中序非递归遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
Status InOrderTraverse1(BiTree T, Status(*Visit)(TElemType e)) //方法1(6.2)
{
SqStack S;
InitStack(S);
BiTNode* p = T;
while(p != NULL || !IsStackEmpty(S))
{
while(p != NULL)
{
Push(S, p);
p = p->lchild;
}
if(!IsStackEmpty(S))
{
p = (BiTNode*)malloc(sizeof(BiTNode));
Pop(S, p); //Here, the problem is how to delete p?
if(!Visit(p->data))
return ERROR;
p = p->rchild;
}
}
DestroyStack(S);
return OK;
}
见上面我的注释//Here, the problem is how to delete p? 在从栈顶取元素时,我们必须传入指针p,但是此p必须在使用前赋值,否则出错!关键是在何时delete p。想了很久,一直没有找到比较好的解决办法???
最终,采用的是另一种方法,即Pop函数实现如下:
/*
* 说明:该函数通过函数返回值将栈顶元素返回
*
*/
BiTNode* Pop(SqStack &S)
{
if(S.base == S.top)
{
return NULL;
}
--S.top;
return S.top;
}
最终,在遍历函数中调用过程如下:
//中序非递归遍历二叉树T,对每个结点调用函数Visit一次且仅一次。
Status InOrderTraverse1(BiTree T, Status(*Visit)(TElemType e)) //方法1(6.2)
{
SqStack S;
InitStack(S);
BiTNode* p = T;
while(p != NULL || !IsStackEmpty(S))
{
while(p != NULL)
{
Push(S, p);
p = p->lchild;
}
if(!IsStackEmpty(S))
{
p = Pop(S); //为什么这个调用可以!
if(!Visit(p->data))
return ERROR;
p = p->rchild;
}
}
DestroyStack(S);
return OK;
}
实际上,我们把问题丢给了编译器,因为函数的返回值是在系统Stack中分配的。
相关文章推荐
- 利用数组名作为函数参数传递排序和用指针进行排序
- CreateThread传递多个参数的方法(利用结构体的参数指针)
- 指针参数是如何传递内存的?
- c/c++中指针参数如何传递内存
- 给初学者提个醒:C/C++函数里的指针参数也是按值传递的!
- 函数参数的传递 动态内存传递问题(指针的指针)
- c语言 函数 用指针传递参数 问题
- C++中函数参数传递(值传递、指针传递,引用传递)
- jna对结构体、指针、引用、拷贝参数传递的使用
- pthread_create 参数传递指针问题
- c/c++中指针参数传递
- 解决利用AJAX从视图传递到Controller 多个参数的接收问题
- 利用apply来进行参数传递和转换
- fork - 利用信号在父子进程间传递指针的实验
- 指针参数是如何传递内存的
- c/c++中指针参数如何传递内存
- 利用指针传递二维数组进行行列式转置
- 指针参数传递实质及二级指针使用
- 成员函数指针作为参数传递给其他函数和普通函数指针的传递
- 利用uboot传递参数进行内核的不同配置