栈有一个很重要的应用:在程序设计语言中讲了递归。那么什么是递归呢?当你往镜子前面一站,镜子里面就有一个你的像。但你试过两面镜子一起照吗?如果A、B两面镜子互相面对面放着,你往中间一站,嘿,两面镜子都有你的千百个“化身”,为什么会有这么奇妙的现象呢?原来,A镜子里有B镜子的像,B镜子里也有A镜子的像,这样反反复复,就会产生一连串的“像中像”。这是一种递归现象。我们先来看一个经典的递归例子:斐波那契数列(Fibonacci)。为了说明这个数列,这位斐老还举了一个很形象的例子。
一、 栈的应用——递归
在高级语言中,调用自己和其他函数并没有本质的不同。我们把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称做递归函数。
函数怎么可以自己调用自己?听起来有些难以理解,不过你可以不要把一个递归函数中调用自己的函数看做是在调用自己,而就当它是在调用另一个函数。只不过,这个函数和自己长得一样而已。当然,写递归程序最怕的就是陷入永不结束的无穷递归中,所以,每个递归都需要一个退出递归的条件。递归使用的是选择结构。递归能使程序的结构更清晰、更简洁、更容易让人理解,从而减少读懂代码的时间。但是大量的递归调用会建立函数的副本,会消耗大量的时间和内存。那么我们讲了那么多递归的内容,和栈有什么关系呢?程序设计基础阶段我们已经了解了递归是如何执行它的前行和退回的。递归过程退回的顺序是它前行顺序的逆序。显然这符合栈的存储方式。简单的说,就是在前行阶段,对于每一层递归,函数的局部变量、参数值以及返回地址都被压如栈中。在退回阶段,位于栈顶的局部变量、参数值和返回地址被弹出,用于返回调用层次中执行代码的其余部分,也就是恢复了调用的状态。当然,对于现在的高级语言,这样的递归问题是不需要用户来管理这个栈的,一切都由系统代劳就可以了。
- 1. ACM算法:n的阶乘
n的阶乘意思是:从一乘到n,是一个连续的乘法问题。其中n是自然数。
实现如下:
int fun(int n) { if(n==1) { return 1; } else { return fun(n-1)*n; } }
- 2. ACM算法:斐波那契数列实现
说如果兔子在出生两个月后,就有繁殖能力,一对兔子每个月能生出一对小兔子来。假设所有兔都不死,那么一年以后可以繁殖多少对兔子呢?
我们拿新出生的一对小兔子分析一下:第一个月小兔子没有繁殖能力,所以还是一对,两个月后,生下一对小兔子数共有两对;三个月后,老兔子又生下一对,因为小兔子还没有繁殖能力,所以一共是三对····依次类推。
那么很简单就推出1,1,2,3,5,8,13····构成了一个序列。这个数列有个十分明显的特点,那是:前面相邻两项之和,构成了后一项。
int fun(int n)//计算斐波那契数列第n项的值 { if(n==1||n==2) { return 1; } else { return fun(n-1)+fun(n-2); } }
- 3. ACM算法:n的k次幂
int fun(int n,int k) { if(k==1) { return n; } else { return fun(n,k-1)*n; } }
- 4. ACM算法:字符串逆转
void strReverse(char s[],int len) { if(len==1) { printf(“%c”,s[0]); } else { printf(“%c”,s[len-1]); strReverse(s,len-1); } }
二、 栈的应用
- 1. 进制转换
在计算机中存储的数据都是二进制,所以往往需要把十进制数据转换成二进制,转换的过程实际就是除2取余数,这其中我们可以看到最先求得余数实际是个位数,书写一个数据的时候都是先书写高位的数据,而后依次到个位。这正好和栈后进先出的特性吻合,因此可以使用栈来存储。
例如:十进制的25转换成2进制
25 25/2 25%2 n 0==n/2 y=n%2
25 12 1
12 6 0
6 3 0
3 1 1
1 0 1
根据最后得到的是高位,先除余得到的是个位,最后得到的二进制值是:11001
- 2. 括号匹配
判断一个表达式的”(“和”)”是否匹配,思路是这样的:遇到”(“则入栈,遇到”)”则从栈顶弹出”(“与之配成一对,当整个表达式扫描完毕时:
(1) 若栈内为空,则说明(与)是匹配的。
(2) 若表达式扫描完毕,栈内仍有(则说明左括号是多的。
(3) 若当)被扫描到,栈里却没有(能弹出了,说明)多,表达式中)也是多的。
- 3. 表达式求值
算法思想:
(1) 首先置操作数栈OPND为空栈,表达式的起始符#为运算符栈OPTR的栈底元素;
(2) 依次读入表达式中的每个字符
若运算符是‘#’或栈顶是‘#’,结束计算,返回OPND栈顶值。
if(是操作数) → 则PUSH( OPND,操作数);
if(是运算符) → 则与OPTR栈顶元素进行比较,按优先级进行操作;
优先级操作细则如下:
① if栈顶元素<输入算符,则算符压入OPTR栈,并接收下一字符
② if栈顶元素=运算符但≠‘#’,则脱括号(弹出左括号)并收下一字;
③ if栈顶元素>运算符,则退栈、按栈顶计算,将结果压入OPND栈。
④ 且该未入栈的运算符要保留,继续与下一个栈顶元素比较!
表达式求值过程的描述:3*(7 – 2 )
栈的技能
/* Note:Your choice is C IDE */ #include "stdio.h" #include "stdlib.h" int e; #define MAX 5 struct person{ int xh; char name[20]; char jg[20]; char tel[20]; }; struct stack1{ struct person arr[MAX]; int top; }s1; struct stack2{ struct person arr[MAX]; int front; int rear; }s2; void menu1(){ printf("+++++++++++++++++++++ "); printf("1.进栈 "); printf("2.出栈 "); printf("3.打印 "); printf("4.退出 "); printf("+++++++++++++++++++++ "); } void menu2(){ printf("+++++++++++++++++++++ "); printf("1.入队 "); printf("2.出队 "); printf("3.打印 "); printf("4.退出 "); printf("+++++++++++++++++++++ "); } void push1(){ if(s1.top==MAX){ printf("溢出! "); }else{ s1.top++; printf("请输入进栈的学生信息: "); printf("请输入学生的学号:"); scanf("%d",&s1.arr[s1.top].xh); printf("请输入学生的姓名:"); scanf("%s",s1.arr[s1.top].name); printf("请输入学生的籍贯:"); scanf("%s",s1.arr[s1.top].jg); printf("请输入学生的学号:"); scanf("%s",s1.arr[s1.top].tel); printf("进栈成功 "); } } void pop1(){ if(s1.top<0){ printf("栈已经空了! "); }else{ e=s1.top; printf("学号:%d,姓名:%s,籍贯:%s,手机号:%s ",s1.arr[e].xh,s1.arr[e].name,s1.arr[e].jg,s1.arr[e].tel); s1.top--; } } void push2(){ if((s2.rear+1)%MAX==s2.front){ printf("溢出! "); }else{ printf("请输入进栈的学生信息: "); printf("请输入学生的学号:"); scanf("%d",&s2.arr[s2.rear].xh); printf("请输入学生的姓名:"); scanf("%s",s2.arr[s2.rear].name); printf("请输入学生的籍贯:"); scanf("%s",s2.arr[s2.rear].jg); printf("请输入学生的学号:"); scanf("%s",s2.arr[s2.rear].tel); s2.rear=(s2.rear+1)%MAX; printf("进队成功 "); } } void pop2(){ if(s2.rear==s2.front){ printf("队列已经空了! "); }else{ e=s2.front; printf("学号:%d,姓名:%s,籍贯:%s,手机号:%s ",s2.arr[e].xh,s2.arr[e].name,s2.arr[e].jg,s2.arr[e].tel); s2.front=(s2.front+1)%MAX; } } void fun1(){ int i,b,flag=0; for(;;){ menu1(); scanf("%d",&b); switch(b){ case 1: push1(); break; case 2: pop1(); break; case 3: if(s1.top<0){ printf("没东西了! "); break; } for(i=0;i<=s1.top;i++){ printf("学号:%d,姓名:%s,籍贯:%s,手机号:%s ",s1.arr[i].xh,s1.arr[i].name,s1.arr[i].jg,s1.arr[i].tel); } break; case 4: flag=1; break; } if(flag==1) { break; } } } void fun2(){ int i,b,flag=0; for(;;){ menu2(); scanf("%d",&b); switch(b){ case 1: push2(); break; case 2: pop2(); break; case 3: if(s2.rear==s2.front){ printf("没东西了! "); break; } for(i=s2.front;i!=s2.rear;i=(i+1)%MAX){ printf("学号:%d,姓名:%s,籍贯:%s,手机号:%s ",s2.arr[i].xh,s2.arr[i].name,s2.arr[i].jg,s2.arr[i].tel); } break; case 4: flag=1; break; } if(flag==1) { break; } } } void main() { int bh; s1.top=-1; s2.front=0; s2.rear=0; for(;;){ printf("1.栈 2.队 3.退出 "); printf("输入编号:"); scanf("%d",&bh); switch(bh){ case 1: fun1(); break; case 2: fun2(); break; case 3: exit(0); default: printf("输入的编号有误! "); } } }