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

|