数据结构-----复习(严蔚敏版)part2_栈

数据结构-----复习(严蔚敏版)part2_栈

栈和队列

    从数据结构角度看,栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表操作的子集,它们是操作受限的线性表,因此,可以成为限定性的数据结构。

栈的定义:

        栈是限定仅在表尾进行插入或删除操作的线性表。因此,对于栈来说,表尾端称为栈顶,相应的,表头端称为栈底。不含元素的空表称为空栈。
        特点:由于栈的修改是按后进先出的原则进行的,因此栈有称为后进先出的线性表。

栈的表示:

        与线性表类似,栈也有两种存储表示方法,顺序栈和链栈。
        顺序栈,即栈的顺序存储结构是利用一组地址连续的存储单元一次存放自栈底到栈顶的数据元素,同时附设指top指示栈顶元素在顺序中的位置。由于栈在使用过程中所需的最大空间的大小难以估计,因此,一般来说在初始化设空栈时不应限定栈的最大容量。合理的做法是:先为栈分配一个基本容量,然后在应用过程中,当栈的空间不够使用时在逐渐扩大,因此可以设定两个常量:存储空间初始分配量(STACK_INIT_SIZE)和存储空间分配增量(STACKINCREMENT),所以可以定义顺序栈:
    
typedef struct{
        SElemType  *base;    //栈底指针,在栈构造之前和销毁之后,base的为NULL
        SElemType   *top;     //栈顶指针
        int  stacksize;        //指示栈的当前可使用的最大容量
    }SqStack;

    基本操作:

Status InitStack(SqStack &S){
        //构造一个空栈S
        S.base = (SElemType *)malloc(STACK_INIT_SIZE *sizeof(SElemType));
        if(!S.base) exit(OVERFLOW);
        S.top = S.base;
        S.stacksize = STACK_INIT_SIZE;
        return OK;
    }
    Status GetTop(SqStack S, SElemType &e){
        //若栈不空,则用e返回S的栈顶元素,并返回OK;否则返回ERROR
        if(S.top == S.base)   return ERROR;
              e = *(S.top - 1);
              return OK;
    }
    Status Push(SqStack &S, SElemType e){
        if(S.top - S.base >= S.stacksize){    //栈满,追加存储空间
            S.base = (SElemType *) realloc (S.base,(S.stacksize + STACKINCREMENT) * sizeof(SElemType));
            if(!S.base) exit(OVERFLOW);
            S.top = S.base + S.stacksize;
            S.stacksize += STACKINCREMENT;
        }
        *S.top++ = e;
        return OK;
    }
    Status Pop(SqStack &S,SElemType &e){
        //若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK,否则返回ERROR
        if(S.top == S.base) return ERROR;
        e = * -- S.top;
        return OK;
    }

栈的应用举例:

1.数制转换

十进制数N和其他d进制数的转换,其中一个简单算法:N = (N div d) * d + N mod d    (其中:div为整除运算,mod为求余运算)
思想:由于计算过程是从低位到高位顺序产生八进制数的各个数位,而打印输出,一般来说应从高位到低位进行,恰好和计算过程相反,因此,若将计算过程中得到八进制数的各位顺序进栈,则按出栈序列打印输出的即为与输入对应的八进制数。
     
   void conversion(){
            //对于输入的任意一个非负十进制整数,打印输出与其等值的八进制数
            InitStack(S);
            scanf("%d",N);
            while(N){
                Push(S,N%8);
                N = N / 8;
            }
            while(!StackEmpty(s)){
                Pop(S,e) ;
                printf("%d",e);   
            }
        }

2.括号匹配的检验

    假设表达式中允许包含两种括号:圆括号和方括号,其嵌套的顺序随意,即([]())或[([][])]等为正确的格式,[(])为不正确的格式。检验括号是否匹配的方法可用“期待的迫切程度”这个概念来描述。
思想:在算法中设置一个站,每读入一个括号,若是右括号,则或者使栈顶的最迫切的期待得以消解,或者是不合法的情况,若是左括号,则作为一个新的更迫切的期待压入栈中,自然是原有的在占中的所有为消解的期待的急迫性都降了一级,并且在算法的开始和结束,栈都应该是空的。
3.行编辑程序
    功能:接受用户从终端输入的程序或数据,并存入用户的数据区。由于用户在终端上进行输入时,不能保证不出差错,因此,若在编辑程序中,“每接受一个字符即存入用户数据区”的做法显然不是最恰当的。较好的做法是,设立一个输入缓冲区,用以接受用户输入的一行字符,然后逐行存入用户数据区。允许用户输入出差错,并在发现有误时及时更改。
    思想:设这个输入缓冲区为一个栈结构,每当从终端接受一个字符之后先作如下判别:如果它既不是退格符也不是退行符,则将该字符压入栈顶;如果是一个退格符,则从栈顶删去一个字符;如果他是一个退行符,则将字符栈清空。
       
 void LineEdit(){
        //利用字符栈S,从终端接受一行并传送至调用过程的数据区。
            InitStack(S);
            ch = getchar();
            while(ch != EOF){    //EOF为全文结束符
                while(ch != EOF && ch != '\n'){
                    switch(ch){
                        case '#' : Pop(S,c);    break;
                        case '@' : CLearStack(S);    break;
                        default  : Push(S,ch);    break;
                    }
                ch = getchar();
                }
                将从栈栈底到栈顶的栈内字符传送至调用过程的数据区
                ClearStack(S);
                if(ch != EOF) ch = getchar();
            }
            DestroyStack(S);
}

3.迷宫求解

基本思想:假设“当前位置”指的是“在搜索过程中某一时刻所在图中某个方块位置”,则求迷宫中一条路径的算法的基本思想是:若当前位置“可通”,则纳入”当前路径“,并继续朝”下一位置“探索,即切换”下一位置“为”当前位置“,如此重复直至到达出口;若当前位置”不可通“,则应顺着”来向“退回到”前一通道块“,然后朝着除”来向“之外的其他方向继续探索;若该通道块的四周4个方块均”不可通“,则应从”当前路径“上删除该通道块。
    假设以栈S记录”当前路径“,则栈顶中存放的是”当前路径上“最后一个通道块。由此,”纳入路径“的操作即为”当前位置入栈“;”从当前路径上删除前一通道块“的操作即为”出栈“。
typedef struct{
    int ord;    //通道块在路径上的”序号“
    PostType seat;    //通道块在迷宫中”坐标位置“
    int di;       //从通道块走向下一个通道块的”方向“
}SElemType        //栈的元素类型
Status MazePath(MazeType maze,PostType start,PostType end){
    //若迷宫maze中存在从入口start到出口end的通道。则求的一条存放在栈中(从栈底到栈顶),并且返回TRUE,否则返回FALSE
    InitStack(S);    curpos = start;
    curstep = 1;
    do{
        if(Pass(curpos)){    //当前位置可以通过,即是未曾走到过的通道块
            FootPrint(curpos);
            e = (curstep,curpos,1);
            Push(S,e);        //加入路径
            if(curpos == end)    return(TURE);
            curpos = NextPos(curpos,1);    //下一个位置是当前位置的东邻
            curstep++;    //探索下一步
        }
        else{    //当前位置不可通过
            if(!StackEmpty(S)){
                Pop(S,e);    //出栈
                while(e.di == 4 && !StackEmpty(S)){
                    MarkPrint(e.seat);Pop(S,e);
                }
                if(e.di < 4){    //探索下一个方向
                    e.di ++;    Push(S,e);
                    curpos = NextPos(e.seat,e.di);    //设定当前位置是该新方向上的相邻块
                }
            }
        }
    }while(!StackEmpty(S));
    return (FALSE);
}

4.表达式求值

是栈应用的又一个典型例子。
为实现算符优先算法,可以使用两个工作栈。一个称作OPTR,用以寄存运算符;另一个称作OPND,用以寄存操作数或计算结果,基本思想:
    (1)首先置操作数栈为空栈,表达式起始符”#“为运算符栈的栈底元素;
    (2)依次读入表达式中每个字符,若是操作数则进OPND栈,若是运算符则和OPTR栈的栈顶运算符比较优先权后做相应操作,直至整个表达式求值完毕(即OPTR栈的栈顶元素和当前读入的字符均为”#“)。
OperandType EvaluateExpression(){
    //算术表达式求值的算符优先算法。设OPTR和OPND分别为运算符栈和运算数栈,OP为运算符集合
    InitStack(OPTR);    Push(OPTR,'#');
    InitStack(OPND);    c = getchar();
    while(c != '#' || GetTop(OPTR) != '#'){
        if(! In(c, OP)) {Push(OPND, c); c = getchar();}    //不是运算符则进栈
        else
            switch (Precede(GetTop(OPTR), c)){    //比较输入的操作符和操作符栈栈顶的运算符优先级
      case '<' :       //栈顶元素优先权低
Push(OPTR, c); c = getchar();
break;  
     case '=':      //脱括号并接受下一个字符
Pop(OPTR, x); c = getchar();
break;
      case '>':       //退栈并将运算结果入栈
Pop(OPTR,theta);
Pop(OPND, b); Pop(OPND, a);
Push(OPND,Operate(a,theta,b));
break;
            }
        }
    return GetTop(OPND);
}

栈和递归的实现

Hanoi塔问题  

void hanoi(int n,char x, char y, char z){
    //将塔座x上按直径由小到大且自上而下编号为1至n的n个圆盘按规矩搬到塔座z上,y可用作辅助塔座。
    //搬动操作move(x,n,z)可定义为(c是初值为0的全局变量,对搬动计数),
    //printf("%i.move disk %i from %c to %c\n",++c,n,x,z);
    if(n == 1)
        move(x,1,z);
    else{
        hanoi(n-1,x,z,y);        //将x上编号为1至n-1的圆盘移到y,z作辅助塔
        move(x,n,z);            //将编号为n的圆盘从x移到z
        hanoi(n-1,y,x,z);       //将y上编号为1至n-1的圆盘移到z,x作辅助塔
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值