由一个例子学习栈

类别:编程语言 点击:0 评论:0 推荐:

 /*
==========================================
作者: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");
}

本文地址:http://com.8s8s.com/it/it29032.htm