栈是一种特殊的线性表。其特殊性在于限定插入和删除数据元素的操作只能在线性表的一端进行。所以具有先进后出的特点。
栈的基本运算有六种:
构造空栈:InitStack(S)、
判栈空: StackEmpty(S)、
判栈满: StackFull(S)、
进栈: Push(S,x)、可形象地理解为压入,这时栈中会多一个元素
退栈: Pop(S) 、 可形象地理解为弹出,弹出后栈中就无此元素了。
取栈顶元素:StackTop(S),不同与弹出,只是使用栈顶元素的值,该元素仍在栈顶不会改变。
由于栈也是线性表,因此线性表的存储结构对栈也适用,通常栈有顺序栈和链栈两种存储结构,这两种存储结构的不同,则使得实现栈的基本运算的算法也有所不同。
这里只对链栈进行介绍:
若是栈中元素的数目变化范围较大或不清楚栈元素的数目,就应该考虑使用链式存储结构。人们将用链式存储结构表示的栈称作"链栈"。链栈通常用一个无头结点的单链表表示。如图所示:
栈的操作是线性表操作的特例。
#include <iostream> #include <stdio.h> #include <stdlib.h> #include <string.h> using namespace std; #define TRUE 1 #define FALSE 0 #define OK 1 #define ERROR 0 #define OVERFLOW -2 typedef struct node//栈的存储结构 { //int num; char num; struct node *ptop; } stack; void push(stack **p,int shu)//入栈 { stack *add=(node *)malloc(sizeof(node)); add->num =shu; add->ptop =*p; *p = add; } void pushchar(stack **p,char shu)//字符入栈,主要针对括号匹配类的题型。 { stack *add=(node *)malloc(sizeof(node)); add->num=shu; add->ptop=*p; *p=add; } void visit(stack *p)//遍历整个链栈 { while(p) { if(p->ptop!=NULL) { cout<<p->num<<" "; } else { cout<<p->num<<endl; } p=p->ptop; } } bool empty(stack *p)//判断栈是否为空 { if(NULL==p) return true; else return false; } int top(stack **p)//返回栈顶元素 { return (*p)->num; } void topchar(stack *p,char *e)//(括号匹配)*e是为了传参 { *e=p->num; } int pop(stack **p)//出栈 { int shu; stack *del= *p; shu=del->num; *p=(*p)->ptop; free(del); return shu; } int main() { node *p=NULL; push(&p,11); push(&p,12); push(&p,13); push(&p,14); ///三个杠注释是在括号匹配下使用 ///char e; ///pushchar(&p,')'); ///pushchar(&p,')'); ///pushchar(&p,'('); ///pushchar(&p,'('); visit(p); ///topchar(p,&e); cout<<e<<endl; cout<<top(&p)<<endl; cout<<pop(&p)<<endl; cout<<pop(&p)<<endl; }
栈的应用
(1) 数制转换//(十进制转换为任意进制)
#include <iostream> #include <stdlib.h> using namespace std; typedef struct _stack { int num; struct _stack *top; } stack; void push(stack* &head,int shu) { stack *add; add=(stack *)malloc(sizeof(stack)); add->num=shu; add->top=head; head=add; } int pop(stack* &head) { int shu; stack *del; shu=(head)->num; del=head; head=(head)->top; free(del); return shu; } void destroy(stack **head) { stack *del; while(*head!=NULL) { del=*head; *head=(*head)->top; free(del); } } void visit(stack *head) { while(head) { if(head->top!=NULL) { cout<<head->num<<" "; } else { cout<<head->num<<endl; } head=head->top; } } int top(stack *head) { return head->num; } bool Isempty(stack *head) { if(head==NULL) return true; else return false; } int main() { char c[]="0123456789ABCDEF"; int n,k; while(cin>>n>>k) { stack *p=NULL; if(n<0) { n=-n; if(k>=2&&k<=10) { while(n!=0) { push(p,n%k); n/=k; } cout<<"-"; while(!Isempty(p)) { cout<<top(p); pop(p); } cout<<endl; } else { while(n!=0) { push(p,n%k); n/=k; } cout<<"-"; while(!Isempty(p)) { cout<<c[top(p)]; pop(p); } cout<<endl; } } else { if(k>=2&&k<=10) { while(n!=0) { push(p,n%k); n/=k; } while(!Isempty(p)) { cout<<top(p); pop(p); } cout<<endl; } else { while(n!=0) { push(p,n%k); n/=k; } while(!Isempty(p)) { cout<<c[top(p)]; pop(p); } cout<<endl; } } destroy(&p); } return 0; }
(2)语法词法分析
(3)表达式求值等//此为大神所写,大家可以借鉴一下
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<math.h> #define true 1 #define false 0 #define OPSETSIZE 8 typedef int Status; unsigned char Prior[8][8] = { // 运算符优先级表 // '+' '-' '*' '/' '(' ')' '#' '^' /*'+'*/'>','>','<','<','<','>','>','<', /*'-'*/'>','>','<','<','<','>','>','<', /*'*'*/'>','>','>','>','<','>','>','<', /*'/'*/'>','>','>','>','<','>','>','<', /*'('*/'<','<','<','<','<','=',' ','<', /*')'*/'>','>','>','>',' ','>','>','>', /*'#'*/'<','<','<','<','<',' ','=','<', /*'^'*/'>','>','>','>','<','>','>','>' }; typedef struct StackChar { char c; struct StackChar *next; }SC; //StackChar类型的结点SC typedef struct Stackdouble { double f; struct Stackdouble *next; }SF; //Stackdouble类型的结点SF SC *Push(SC *s,char c) //SC类型的指针Push,返回p { SC *p=(SC*)malloc(sizeof(SC)); p->c=c; p->next=s; return p; } SF *Push(SF *s,double f) //SF类型的指针Push,返回p { SF *p=(SF*)malloc(sizeof(SF)); p->f=f; p->next=s; return p; } SC *Pop(SC *s) //SC类型的指针Pop { SC *q=s; s=s->next; free(q); return s; } SF *Pop(SF *s) //SF类型的指针Pop { SF *q=s; s=s->next; free(q); return s; } double Operate(double a,unsigned char theta, double b) //计算函数Operate { switch(theta) { case '+': return a+b; case '-': return a-b; case '*': return a*b; case '/': return a/b; case '^': return pow(a,b); default : return 0; } } char OPSET[OPSETSIZE]={'+','-','*','/','(',')','#','^'}; Status In(char Test,char *TestOp) { int Find=false; for (int i=0; i< OPSETSIZE; i++) { if(Test == TestOp[i]) Find= true; } return Find; } Status ReturnOpOrd(char op,char *TestOp) { for(int i=0; i< OPSETSIZE; i++) { if (op == TestOp[i]) return i; } } char precede(char Aop, char Bop) { return Prior[ReturnOpOrd(Aop,OPSET)][ReturnOpOrd(Bop,OPSET)]; } double EvaluateExpression(char* MyExpression) { // 算术表达式求值的算符优先算法 // 设OPTR和OPND分别为运算符栈和运算数栈,OP为运算符集合 SC *OPTR=NULL; // 运算符栈,字符元素 SF *OPND=NULL; // 运算数栈,实数元素 char TempData[20]; double Data,a,b; char theta,*c,Dr[]={'#','\0'}; OPTR=Push(OPTR,'#'); c=strcat(MyExpression,Dr); strcpy(TempData,"\0");//字符串拷贝函数 while (*c!= '#' || OPTR->c!='#') { if (!In(*c, OPSET)) { Dr[0]=*c; strcat(TempData,Dr); //字符串连接函数 c++; if (In(*c, OPSET)) { Data=atof(TempData); //字符串转换函数(double) OPND=Push(OPND, Data); strcpy(TempData,"\0"); } } else // 不是运算符则进栈 { switch (precede(OPTR->c, *c)) { case '<': // 栈顶元素优先级低 OPTR=Push(OPTR, *c); c++; break; case '=': // 脱括号并接收下一字符 OPTR=Pop(OPTR); c++; break; case '>': // 退栈并将运算结果入栈 theta=OPTR->c;OPTR=Pop(OPTR); b=OPND->f;OPND=Pop(OPND); a=OPND->f;OPND=Pop(OPND); OPND=Push(OPND, Operate(a, theta, b)); break; } //switch } } //while return OPND->f; } //EvaluateExpression int main() { char s[128]; puts("请输入表达式:"); gets(s); puts("该表达式的值为:"); printf("%s\b=%g\n",s,EvaluateExpression(s)); return 0; }
栈与递归(经典例题汉诺塔):
汉诺塔的问题:
解决:
1)如果有一个盘子,直接从X移到Z即可。
2)如果有n个盘子要从X移到Z,Y作为辅助。问题可以转化为,先将上面n-1个从X移动到Y,Z作为辅助,然后将第n个从X移动到Z,最后将剩余的n-1个从Y移动到Z,X作为辅助。
如果要计算要进行多少次操作,有个简单的方法公式2^n-1,n是盘子的数量。
#include<stdio.h> void move(int n,char a,char b,char c) { if(n==1) printf("\t%c->%c\n",a,c); //当n只有1个的时候直接从a移动到c else { move(n-1,a,c,b); //第n-1个要从a通过c移动到b printf("\t%c->%c\n",a,c); move(n-1,b,a,c); //n-1个移动过来之后b变开始盘,b通过a移动到c,这边很难理解 } } main() { int n; printf("请输入要移动的块数:"); scanf("%d",&n); move(n,'a','b','c'); }