4.1 Stack(栈)

出现的问题 参考的一些博客
c++模板类在继承中子类无法访问父类的成员的问题

解答: this访问即可,也可以直接在成员前加基类的名字Base::

4.1.1 stack的ADT接口

操作接口功能
size()报告栈的规模
empty()判断栈是否为空
push(e )将e插入至栈顶
pop( )删除栈顶元素
top( )引用栈顶元素

4.1.2 stack模板类

基于向量的类模板

#include "Vector.h"//基于之前写的类模板Vector
template <typename T> class Stack: public Vector<T> { //由向量派生的栈模板类
public: //size()、empty()以及其它开放接口均可直接沿用
 void push( T const & e ) { Vector<T>::insert( e ); } //入栈
 T pop() { return Vector<T>::remove( Vector<T>::size() - 1 ); } //出栈
 T & top() { return (*this)[ Vector<T>::size() - 1 ]; } //取顶
}; //以向量首/末端为栈底/顶——颠倒过来呢?

4.1.3 stack的典型应用

4.1.3.1 逆序输出
4.1.3.1.1进制转换
//省略Stack类模板
void convert( Stack<char> & S, __int64 n, int base ) {//数的值为n,进制为 base
 char digit[] = "0123456789ABCDEF"; //数位符号,如有必要可相应扩充
 while ( n > 0 ) { //由低到高,逐一计算出新进制下的各数位
 S.push( digit[ n % base ] ); //余数(当前的数位)入栈
 n /= base; //n更新为其对base的除商
 }
} //新进制下由高到低的各数位,自顶而下保存于栈S中
int  main()
{
    n=10000;//表示要转换进制的数的值
    base=10;//表示将要转换的进制
    Stack<char> S; convert( S, n, base); //用栈记录转换得到的各数位
    while ( ! S.empty() ) printf( "%c", S.pop() ); //逆序输出
    return 0;
}
4.1.3.1 递归嵌套
4.1.3.1.2括号匹配
//截取表达式中不含括号的头部和尾部
void trim (const char exp[], int &lo, int& hi){
	while((lo <= hi) && (exp[lo] != '(') && (exp[lo] != ')'))
		lo++;          //查找第一个括号
	while((lo <= hi) && (exp[hi] != ')') && (exp[hi] != '('))
		hi--;         //寻找最后一个括号
}

//寻找拆分点
int divide (const char exp[], int lo, int hi){
	int mi = lo; int crc = 1;  //crc为[lo, mi]内左右括号数目之差
	if ((0 < crc) && (++mi < hi)){
		if (exp[mi] == ')') crc--;
		if (exp[mi] == '(') crc++;
	}
	return mi;
}

bool paren (const char exp[], int lo, int hi){
	trim(exp, lo, hi);
	if(lo > hi) return false;  //也就是表达式没有括号,此时,lo = hi, hi = hi-1
	if (exp[lo] != '(') return false;  //表达式非法
	if (exp[hi] != ')') return false;  //表达式非法
	int mi = divide(exp, lo, hi);
	if (mi > hi) return false;  // 拆分点错误
	return paren(exp, lo+1, mi-1) && paren(exp, mi+1, hi);
}
4.1.3.3 延迟缓冲及逆波兰表达式
4.1.3.3.1优先级表
#define N_OPTR 9 //运算符号总数
typedef enum{ADD,SUB,MUL,DIV,POW,FAC,L_P,R_P,EOE}Operator;//运算符集合

const char pri[N_OPTR][N_OPTR] = { //运算符优先等级 [栈顶][当前]
 /*         |--------------- 当前运算符 --------------*/
 /*          +    -    *    /    ^    !    (    )   \0  */
 /* -- + */ '>', '>', '<', '<', '<', '<', '<', '>', '>',
 /* | -  */ '>', '>', '<', '<', '<', '<', '<', '>', '>',
 /* 栈 * */ '>', '>', '>', '>', '<', '<', '<', '>', '>',
 /* 顶 / */ '>', '>', '>', '>', '<', '<', '<', '>', '>',
 /* 运 ^ */ '>', '>', '>', '>', '>', '<', '<', '>', '>',
 /* 算 ! */ '>', '>', '>', '>', '>', '>', ' ', '>', '>',
 /* 符 ( */ '<', '<', '<', '<', '<', '<', '<', '=', ' ',
 /* |  ) */ ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',
 /* -- \0*/ '<', '<', '<', '<', '<', '<', '<', ' ', '='
};
4.1.3.3.2通过优先级表和emun判断优先级
//typedef enum{ADD,SUB,MUL,DIV,POW,FAC,L_P,R_P,EOE}Operator;//运算符集合
Operator optr2rank(char op){//由运算符转译出编号
    switch(op){
        case '+': return ADD;//加
        case '-': return SUB;//减
        case '*': return MUL;//乘
        case '/': return DIV;//除
        case '^': return POW;//乘方
        case '!': return FAC;//阶乘
        case '(': return L_P;//左括号
        case ')': return R_P;//右括号
        case '\0':return EOE;//起始符和终止符
        default : exit(-1);//未知运算符
    }
}

char orderBetween(char op1,char op2){
    return pri[optr2rank(op1)][optr2rank(op2)];
}
4.1.3.3.3解析当前操作数
//解析当前操作数,要引入头文件#include<ctype.h>使用isdigit()函数
void readNumber(char *& p,Stack<double>& stk){//将起始于p的子串解析为数值,并存入操作数栈
    stk.push((double)(*p - '0'));//当前数位对应的数值入栈
    while(isdigit(*(++p)))//只要后续还有紧邻的数字(即多位整数的情况),则
        stk.push(stk.pop()*10+(*p-'0'));//弹出原操作数并追加新数位后,新数值直接入栈
    if('.'!=(*p)) return ;//此后非小数点意味当前操作数解析完成
    double fraction=1;//否则意味还有小数部分
    while(isdigit(*(++p)))//逐位加入
    stk.push(stk.pop()+(*p-'0')*(fraction/=10));//小数部分
}
4.1.3.3.4计算值
__int64 facI(int n)
{
    __int64 f = 1;
    while (n > 1)
    {
        f *= n--;
    }
    return f;
} //阶乘运算(迭代版)
double calcu(double a, char op, double b) 
{ //执行二元运算
    switch (op) 
    {
        case '+': 
        {
            return a + b;
        }
        case '-': 
        {
            return a - b;
        }
        case '*':
        {
            return a * b;
        }
        case '/': 
        {
            if (0 == b)
            {
                exit(-1);
            }
            else
            {
                return a / b; //注意:如此判浮点数为零可能不安全
            }
        }
        case '^':
        {
            return pow(a, b);
        }
        default:
        {
            exit(-1);
        }
    }
}
double calcu(char op, double b) 
{ //执行一元运算
    switch (op) 
    {
        case '!':
        {
            return (double)facI((int)b); //目前仅有阶乘,可照此方式添加
        }
        default: 
        {
            exit(-1);
        }
    }
}
4.1.3.3.5将当前操作数或者操作符号连接到RPN
void append(char *& rpn, float opnd){//操作数
	int n = strlen(rpn);
	char buf[64];
	if (opnd != (float)(int)opnd) 
		sprintf(buf, "%.2f \0", opnd);
	else
		sprintf(buf, "%d \0", (int)opnd);
	rpn = (char*) realloc(rpn, sizeof(char)*(n + strlen(buf) + 1));
	strcat(rpn, buf);
}
void append(char *& rpn, char optr){//操作符
	int n = strlen(rpn);
	rpn = (char*) realloc(rpn, sizeof(char)*(n + 3));
	sprintf(rpn + n, "%c ", optr);
	rpn[n + 2] = '\0';
}
4.1.3.3.6对表达式求值,并转化为逆波兰表达式
double evaluate(char* S, char*& RPN)
{ //对(已剔除白空格的)表达式S求值,并转换为逆波兰式RPN
    Stack<double> opnd;
    Stack<char> optr; //运算数栈、运算符栈 /*DSA*/任何时刻,其中每对相邻元素之间均大小一致
    char* expr = S;
    optr.push('\0'); //尾哨兵'\0'也作为头哨兵首先入栈
    while (!optr.empty())
    { //在运算符栈非空之前,逐个处理表达式中各字符
        if (isdigit(*S))
        { //若当前字符为操作数,则
            readNumber(S, opnd);
            append(RPN, opnd.top()); //读入操作数,并将其接至RPN末尾
        }
        else //若当前字符为运算符,则
        {
            switch (orderBetween(optr.top(), *S))
            { //视其与栈顶运算符之间优先级高低分别处理
                case '<': //栈顶运算符优先级更低时
                {
                    optr.push(*S); S++; //计算推迟,当前运算符进栈
                    break;
                }
                case '=': //优先级相等(当前运算符为右括号或者尾部哨兵'\0')时
                {
                    optr.pop(); S++; //脱括号并接收下一个字符
                    break;
                }
                case '>':
                { //栈顶运算符优先级更高时,可实施相应的计算,并将结果重新入栈
                    char op = optr.pop(); append(RPN, op); //栈顶运算符出栈并续接至RPN末尾
                    if ('!' == op)
                    { //若属于一元运算符
                        double pOpnd = opnd.pop(); //只需取出一个操作数,并
                        opnd.push(calcu(op, pOpnd)); //实施一元计算,结果入栈
                    }
                    else
                    { //对于其它(二元)运算符
                        double pOpnd2 = opnd.pop(), pOpnd1 = opnd.pop(); //取出后、前操作数 /*DSA*/提问:
                                                                        //可否省去两个临时变量?
                        opnd.push(calcu(pOpnd1, op, pOpnd2)); //实施二元计算,结果入栈
                    }
                    break;
                }
                default:
                {
                    exit(-1); //逢语法错误,不做处理直接退出
                }
            }

        }
    }
    return opnd.pop(); //弹出并返回最后的计算结果
}
4.1.3.3.7逆波兰表达式求值算法
double rpnEvaluation(char *expr){//对RPN表达式的求值
    stack<double> Stk;//存放操作数的栈
    while((*expr)!='\0'){//expr尚未扫描完毕,即不等于结束符号,这里的结束符号是'\0'
        if((*expr)==' ') {//这里的分隔符号是空格
        expr++;//跳过分隔符号
        continue;
        }
        if (isdigit(*expr))//
        { //若当前字符为操作数,则
            readNumber(expr,Stk);//将操作数入栈
        }else //若当前字符为运算符,则
        {
            char op =(*expr);//计算当前操作符
            expr++;//运算完成之后计算表达式
             if ('!' == op){//如果是一元运算
                expr++;//运算完成之后计算表达式
                double number = Stk.top();
                Stk.pop();
                Stk.push(calcu(op, number)); //实施一元计算,结果入栈
             }
             else{
                double pOpnd2 = Stk.top();
                Stk.pop();
                double pOpnd1 = Stk.top();
                Stk.pop();
                Stk.push(calcu(pOpnd1, op, pOpnd2)); //实施二元计算,结果入栈
             }

        }
    }
	return Stk.top();
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值