• 全部
  • 问答

栈应用详解(由一个例子学习栈)学习栈的朋友请进!

rerli 2003-12-02 02:58:23
/*
作者:rerli
时间:2003-12-02
目的:由一个例子学习栈。

栈应用详解:
用栈改递归为非递归,计算Ackermann函数A(n,x,y)。

*/


#include <stdlib.h>
#include <stdio.h>
#define NULL 0

typedef struct node
{
int n;
double x;
double y;
}NODE;

#define LEN sizeof(NODE)

/*栈的需要变量*/
typedef enum {false,true}bool; /*定义bool类型*/
static NODE *p_stack; /*定义栈指针变量*/
static NODE *stack; /*定义一个栈*/
static unsigned int stack_size; /*栈的大小*/

/*
栈空 出栈前 出栈后
| | | | | |
|_____| p_stack--->|_____| |_____|
|_____| |__1__| p_stack--->|__1__|
p_stack--->|_____| |__0__| |__0__|

从上图可知出栈后,栈顶指针指向的是刚才出栈元素的首地址。
若POP()栈顶值,此时的*p_stack和刚才pop()出来的值一模一样。

*/


/*
功能:初始化栈的大小
返回:true or false
*/
bool init_stack(unsigned int size)
{
stack=(NODE *)malloc(size*LEN);
if (stack==NULL)
{
return false;
}
stack_size=size;
p_stack = stack;
return true;
}

/*
功能:释放栈的内存
返回:void
*/
void free_stack()
{
free(stack);
stack = NULL;
}

/*
功能:判断栈是否已满
返回:true or false
*/
bool full()
{
return (p_stack-stack)>=stack_size;
}

/*
功能:判断栈是否为空
返回:true or false
*/
bool empty()
{
return (p_stack==stack);
}

/*
功能:入栈
返回:true or false
*/
bool push(NODE p)
{
if (!full())
{
*p_stack=p;
p_stack++;
return true;
}
else
{
printf("stack is overflow !\n");
return false;
}
}

/*
功能:出栈
返回:出栈元素
*/
NODE pop()
{
if (!empty())
{
return *(--p_stack);
}
else
{
printf("stack is empty !\n");
}
}



/*
功能:用栈改递归为非递归,计算Ackermann函数A(n,x,y)。
返回:double 计算结果
*/
double Ackermann(NODE node)
{
double B;
NODE tnode;

if (!full())
{
push(node);
}

while (!empty())
{
tnode = pop();
while (tnode.n != 0 && tnode.y != 0) /*递推,将计算参数进栈*/
{
/*修改栈顶节点的字段值,如果不好理解的话,可以参考上面的图示*/
(*p_stack).n--;
(*p_stack).y = (*p_stack).x;
(*p_stack).x = -1.0; /*表示x值不确定*/

/*新求得的节点值进栈*/
tnode.y -= 1.0;
if (!push(tnode)) /*将下一步进栈*/
{
return -1.0;
}
}

if (!empty())
{
tnode = pop();
}
if (tnode.n == 0)
{
B = tnode.x + 1.0;
}
else if (tnode.n == 1)
{
B = tnode.x;
}
else if (tnode.n == 2)
{
B = 0.0;
}
else if (tnode.n == 3)
{
B = 1.0;
}
else
{
B = 2.0;
}

if (!empty())
{
(*p_stack).x = B; /*栈不为空,则栈顶节点的x值改为B*/
}
}
return B;
}


void main(void)
{
int i;
NODE node1 = {3,2,1}, node2 = {4,1,2}, node3;
bool success=init_stack(2);
if (!success)
{
exit(0);
}

printf("Ackermann(%d,%d,%d) = %.2f\n",node1.n,(int)node1.x,(int)node1.y,Ackermann(node1));

/*
push(node1);
push(node2);
printf("n=%d, x=%8f, y=%8f\n",(*p_stack).n,(*p_stack).x,(*p_stack).y);
node3 = pop();
printf("n=%d, x=%8f, y=%8f\n",(*p_stack).n,(*p_stack).x,(*p_stack).y);
printf("n=%d, x=%8f, y=%8f\n",node3.n,node3.x,node3.y);
*/

free_stack(); /*注意程序退出时释放栈内存*/

printf("\n");
system("pause");
}
...全文
154 点赞 收藏 10
写回复
10 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
tass 2003-12-08
up
回复
rerli 2003-12-04
谢谢鼓励!
回复
chaonet 2003-12-04
up up up
回复
ZhangYv 2003-12-03
具备先进后出特性的任何信息都可以使用栈来表述
回复
wildhorseych 2003-12-03
在一般应用中,什么情况下需要用到栈?
回复
fmonkey 2003-12-03

希望以后能看到更多这样的文章
谢谢搂主
回复
boy8765 2003-12-03
回复
rerli 2003-12-03
/*
=================================================================
作者:rerli
时间:2003-12-03
目的:由一个例子学习栈。

栈应用详解:

栈的实现可以用数组,亦可用链表。
下面讲讲这两种实现栈结构的详细程序,并分别看看:
用栈改递归为非递归,计算Ackermann函数A(n,x,y)。
==================================================================
*/

/*
==================================================================
第二种:链表
用链表实现的栈称为链接栈。很显然链接栈没有栈满之说了,它是来者不拒啊!
栈顶是链表的第一个元素;
栈底是链表的最后一个元素;
链表的头指针就是栈顶指针。
===================================================================
*/

#include <stdlib.h>
#include <stdio.h>
#define NULL 0

typedef struct node
{
int n;
double x;
double y;
}NODE;

typedef struct lnode
{
NODE data;
struct lnode *next;
}LNODE;

#define LEN sizeof(LNODE)

/*栈的需要变量*/
typedef enum {false,true}bool; /*定义bool类型*/
static LNODE *p_stack=NULL; /*定义栈指针变量*/

/*
==================================================================
栈空
| |
|_____|
|_____|
|_____|
NUll <---p_stack

出栈前
| |
|_____|
|__1__|<---p_stack
|__0__|

出栈后
| |
|_____|
|__1__|
|__0__|<---p_stack

从上图可知:
1、出栈前,栈顶指针指向的是栈顶元素的地址;
2、出栈后,栈顶指针指向的是原栈顶元素下一个元素的地址。
3、这种链接栈显然更符合栈的定义。相比之下,它比数组(或指针)更容易理解一些。
4、显然若我们用POP()栈顶值后,用*p_stack取值,则得到值不和pop()出来的值一模一样。
5、注意到上面的4和前面数组(或指针)栈的4有很大的差别就是根据栈顶指针取出来的值不同,
这样导致我们可以用数组(或指针)栈可以方便实现求Ackermann函数A(n,x,y),而链接栈则
不能实现,除非我们改结构。
======================================================================
*/

/*
======================================================================
注意到链接栈比数组(或指针)栈少了几个函数,上面的变量也少了。
======================================================================
*/

/*
==============================
功能:判断栈是否为空
返回:true or false
==============================
*/
bool Empty()
{
/*栈顶指针等于NULL,则栈空*/
return (p_stack==NULL);
}

/*
==============================
功能:入栈
返回:true or false
==============================
*/
bool Push(NODE p)
{
static LNODE *stack=NULL;
stack = (LNODE *)malloc(LEN); /*开辟一个节点的空间*/

if (stack != NULL)
{
stack->data = p; /*入栈*/
stack->next = p_stack; /*新节点的next指向原栈顶*/
p_stack = stack; /*栈顶指针指向新节点*/

free(stack); /*释放栈的内存*/
stack = NULL;
return true;
}
else
{
return false;
}
}

/*
==============================
功能:出栈
返回:出栈元素
==============================
*/
NODE Pop()
{
NODE p;

if (!Empty())
{
p = p_stack->data; /*出栈元素*/
p_stack = p_stack->next; /*更改栈顶指针*/
return p;
}
else
{
printf("stack is empty !\n");
}
}


void main(void)
{
NODE node1 = {3,1,2};

Push(node1);
printf("n=%d, x=%.2f, y=%.2f\n",(p_stack->data).n,(p_stack->data).x,(p_stack->data).y);

Pop();
/*根据下面的语句结果,我们可以判断以上实现的栈是否正确的:1正确,其他错误。*/
printf("%d",p_stack==NULL);

printf("\n");
system("pause");
}
回复
rerli 2003-12-03

谢谢楼上两位的支持!
ZhangYv(永不放弃) 说得很对!具备先进后出特性的任何信息都可以使用栈来表述。
但是不同的栈有不同的用途:
看看我的修改稿:
/*
==========================================
作者:rerli
时间:2003-12-03
目的:由一个例子学习栈。

栈应用详解:

栈的实现可以用数组,亦可用链表。
下面讲讲这两种实现栈结构的详细程序,并分别看看:
用栈改递归为非递归,计算Ackermann函数A(n,x,y)。
===========================================
*/

/*
===========================================
第一种:数组
可以看出,下面的程序并没有真正用数组去实现,
而是使用指针。使用指针操作起来比数组稍微灵活
一点。下面的实现你将体会到。

栈底就是初始化后stack的首地址;
栈顶就是最后一个入栈元素的尾地址。
栈顶指针就是地址指针。
===========================================
*/


#include <stdlib.h>
#include <stdio.h>
#define NULL 0

typedef struct node
{
int n;
double x;
double y;
}NODE;

#define LEN sizeof(NODE)

/*栈的需要变量*/
typedef enum {false,true}bool; /*定义bool类型*/
static NODE *p_stack=NULL; /*定义栈指针变量*/
static NODE *stack=NULL; /*定义一个栈*/
static unsigned int stack_size=0; /*栈的大小*/

/*
=========================================================
栈空
| |
|_____|
|_____|
|_____|
NUll <---p_stack

出栈前
| |
|_____|<---p_stack
|__1__|
|__0__|

出栈后
| |
|_____|
|__1__|<---p_stack
|__0__|

从上图可知:
1、出栈前,栈顶指针没有指向任何元素;
2、出栈后,栈顶指针指向的是刚才出栈元素的首地址。
3、这种数组(或指针)栈描述显然有点不符合栈的定义,即栈顶指针并不是真正意义的指向栈顶(有点难懂,但从图中可以很快悟出)。
4、显然若我们用Pop()栈顶值后,用*p_stack取值,则必然得到Pop()出来的值一模一样。
==========================================================
*/


/*
========================
功能:初始化栈的大小
返回:true or false
========================
*/
bool InitStack(unsigned int size)
{
stack=(NODE *)malloc(size*LEN); /*开辟空间*/
if (stack==NULL) /*开辟空间失败,则返回false*/
{
return false;
}
stack_size = size; /*保存栈空间大小值*/
p_stack = stack; /*栈顶指针赋初值*/
return true; /*初始化成功,返回true*/
}

/*
======================
功能:释放栈的内存
返回:void
======================
*/
void FreeStack()
{
free(stack);
/*
注意:这一点很重要。free()之后并不能将stack
置为NULL,所以我们一定要自己做。这样能防止产生
“野指针”,即地址不确定的指针。
*/
stack = NULL;
}

/*
==========================
功能:判断栈是否已满
返回:true or false
==========================
*/
bool Full()
{
/*栈顶指针与栈的初始地址的差,如果大于等于空间值,则此时栈满*/
return (p_stack-stack)>=stack_size;
}

/*
===========================
功能:判断栈是否为空
返回:true or false
===========================
*/
bool Empty()
{
/*栈顶指针与栈的初始地址的相等,则栈空*/
return (p_stack==stack);
}

/*
========================
功能:入栈
返回:true or false
========================
*/
bool Push(NODE p)
{
if (!Full()) /*栈不满,则入栈;栈顶指针要上移*/
{
*p_stack = p;
p_stack++;
return true;
}
else
{
printf("stack is overflow !\n");
return false;
}
}

/*
===================
功能:出栈
返回:出栈元素
===================
*/
NODE Pop()
{
if (!Empty()) /*栈不空,则出栈;栈顶指针要下移*/
{
return *(--p_stack);
}
else
{
printf("stack is empty !\n");
}
}



/*
===================================================
功能:用栈改递归为非递归,计算Ackermann函数A(n,x,y)。
返回:double 计算结果
===================================================
*/
double Ackermann(NODE node)
{
double B;
NODE tnode;

if (!Full())
{
Push(node);
}

while (!Empty())
{
tnode = Pop();
while (tnode.n != 0 && tnode.y != 0) /*递推,将计算参数进栈*/
{
/*修改栈顶节点的字段值,如果不好理解的话,可以参考上面的图示*/
p_stack->n--;
p_stack->y = p_stack->x;
p_stack->x = -1.0; /*表示x值不确定*/

/*新求得的节点值进栈*/
tnode.y -= 1.0;
if (!Push(tnode)) /*将下一步进栈*/
{
return -1.0;
}
}

if (!Empty())
{
tnode = Pop();
}
if (tnode.n == 0)
{
B = tnode.x + 1.0;
}
else if (tnode.n == 1)
{
B = tnode.x;
}
else if (tnode.n == 2)
{
B = 0.0;
}
else if (tnode.n == 3)
{
B = 1.0;
}
else
{
B = 2.0;
}

if (!Empty())
{
p_stack->x = B; /*栈不为空,则栈顶节点的x值改为B*/
}
}
return B;
}


void main(void)
{
NODE node1 = {3,1,2};

if (!InitStack(50)) /*初始化不成功,则退出*/
{
exit(0);
}

printf("Ackermann(%d,%d,%d) = %.2f\n",node1.n,(int)node1.x,(int)node1.y,Ackermann(node1));

FreeStack(); /*注意程序退出时释放栈内存*/

printf("\n");
system("pause");
}
回复
rerli 2003-12-02
/*图片乱了整理如下:
栈空 出栈前 出栈后
| | | | | |
|_____|p_stack-->|_____| |_____|
|_____| |__1__| p_stack-->|__1__|
p_stack-->|_____| |__0__| |__0__|

从上图可知出栈后,栈顶指针指向的是刚才出栈元素的首地址。
若POP()栈顶值,此时的*p_stack和刚才pop()出来的值一模一样。

*/
回复
相关推荐
发帖
C语言
创建于2007-09-28

6.3w+

社区成员

C语言相关问题讨论
申请成为版主
帖子事件
创建了帖子
2003-12-02 02:58
社区公告
暂无公告