C++数据结构(二):栈

C++数据结构(二)栈

定义

只允许在同一端插入, 同一端删除的线性表
允许插入删除的一端叫栈顶, 另一端叫栈底

特性

先进后出(FILO)

ADT Stack{
元素关系: 线性关系(a1, a2, …, an)
操作:
bool push(e); //入栈
bool pop(); //出栈
T& top(); //取栈顶
bool empty(); //判断是否空
void clear(); //清空
}

实现

顺序栈

用一块连续存储空间(数组)存储所有元素
只能在一端进行插入删除

template<typename T>
class SqStack{
    T *data, *top_; //栈底顶指针 
    int capacity; //容量
public:
    //构造函数
    SqStack(int cap = 3){
        data = new T[cap];
        if (!data){
            top_ = 0;
            capacity = 0;
            return;
        }
        top_ = data;
        capacity = cap;
    }
    T& top(); //返回栈顶元素
    bool push(T e); //压栈
    bool pop(); //出栈
    void clear(); //清空
    bool empty(); //查看是否为空
};

//返回栈顶元素
template<typename T>
T& SqStack<T>::top(){
    //顶底位置同为空栈
    if (top_ == data) throw "空栈";
    //否则返回顶元素
    return *(top_ - 1);
}

//压栈
template<typename T>
bool SqStack<T>::push(T e){
    //检查空间不够申请
    if (top_ - data == capacity){
        T *p = new T[2 * capacity];
        if (!p) return false;
        for (int i = 0; i < capacity; i++)
            p[i] = data[i];
        delete[] data;
        data = p;
        top_ = data + capacity;
        capacity = 2 * capacity;
    }
    //加入元素移动栈顶指针
    *top_ = e;
    top_++;
    return true;
}

//出栈
template<typename T>
bool SqStack<T>::pop(){
    if (top_ == data)
        return false;//空栈
    top_ --;
    return true;
}

//清空
template<typename T>
void SqStack<T>::clear(){
    top_ = data;
}

//查看是否为空
template<typename T>
bool SqStack<T>::empty(){
    return top_ == data;
}

STL的stack也包含以上所有实现

栈的应用

  1. 数制转换
    十进制数N = a_k* d^k + … + a_1* d^1 + a_0
    转换方法: N除d取余数倒序, 借助栈先进后出可以按正确顺序输出
#include <iostream>
#include <string>
#include "SqStack.hpp"

using namespace std;

int main(){
    int N, d=8;
    cin >> N;
    SqStack<int> S;
    while (N){
        S.push(N % d);
        N = N / d;
    }

    while (!S.empty()){
        int e = S.top();
        cout << e << ' ';
        S.pop();
    }
    return 0;
}
  1. 括号匹配检测
    括号, 引号等是成对出现的必须相互匹配
    从左到右遍历括号, 先遇到的左括号最后匹配, 级遇左括号压栈, 遇见右括号出栈并匹配
    栈即存放了扫描历史, 还可以找到最近左括号
#include <iostream>
#include <string>
#include "SqStack.hpp"

using namespace std;

bool isLeft_kuohao(char ch){
    return ch=='('||ch=='['||ch=='{';
}

bool isRight_kuohao(char ch){
    return ch==')'||ch==']'||ch=='}';
}

bool is_match(const char c1, const char c2){
    if (c1 == '('&& c2 == ')') return true;
    if (c1 == '['&& c2 == ']') return true;
    if (c1 == '{'&& c2 == '}') return true;
    return false;
}

int main(){
    string s;
    cin >> s;
    SqStack<char> stack;
    for (int i = 0; i < s.size(); i++){
        //左括号压栈
        if(isLeft_kuohao(s[i])){
            stack.push(s[i]);
        }
        //右括号看栈是否空, 空-1, 不空看是否匹配, 不匹配返回-2, 匹配出栈
        else if(isRight_kuohao(s[i])){
            if(stack.empty())
                return -1;
            char c = stack.top();
            if (!is_match(c, s[i]))
                return -2;
            stack.pop();
        }
        else {}
    }
    //退出循环栈空匹配成功
    if(stack.empty())
        cout << s << "匹配!\n";
}
  1. 表达式求值
    两个栈分别保存运算数和运算符
    遍历表达式依次入栈, 若运算符优先级低于栈顶则先不入运算符栈, 而是运算符栈顶出栈运算数栈取出两个运算数计算后结果入运算数栈, 当前运算符继续与栈顶运算符比较. 右括号遇见左括号抵消, 左括号出栈, 遍历结束后依次出栈计算
#include <iostream>
#include <string>
#include "SqStack.hpp"

using namespace std;
//优先级表格
char op_prior_table[][7] = {
    {'>', '>', '<', '<', '<', '>', '>', },
    {'>', '>', '<', '<', '<', '>', '>', },
    {'>', '>', '>', '>', '<', '>', '>', },
    {'>', '>', '>', '>', '<', '>', '>', },
    {'<', '<', '<', '<', '<', '=', '!', },
    {'>', '>', '>', '>', '!', '>', '>', },
    {'<', '<', '<', '<', '<', '!', '=', },
};

//把运算符映射到优先级表格
int index_of_op(const char op){
    if      (op == '+') return 0;
    else if (op == '-') return 1;
    else if (op == '*') return 2;
    else if (op == '/') return 3;
    else if (op == '(') return 4;
    else if (op == ')') return 5;
    else                return 6; //#
    
}

//计算亮运算符优先级
int prior(const char op1, const char op2){
    int i = index_of_op(op1), j = index_of_op(op2);
    char cmp = op_prior_table[i][j];
    if (cmp == '>') return 1;
    else if (cmp == '<') return -1;
    else if (cmp == '=') return 0;
    return 10000; //不会出现的情况
}

//用算符op对量运算数运算 n1 op n2
int op2num(const char op, int n1, int n2){
    if (op == '+') return n1 + n2;
    else if (op == '-') return n1 - n2;
    else if (op == '*') return n1 * n2;
    else if (op == '/') return n1 / n2;
    return 0;
}

int main(){
    SqStack<int> nS;
    SqStack<char> oS;
    string exp="1+2*3/(5-4)#";
    oS.push('#');
    //遍历表达式
    for (int i = 0; i < exp.size(); ){
        //运算数入运算数栈
        if (exp[i]>='0' && exp[i]<='9'){
            nS.push(exp[i]-'0'); 
            i++;
        }
        //运算符
        else{
            auto op1 = oS.top();
            int ret = prior(op1, exp[i]);
            //当前优先, 入栈
            if(ret < 0){
                oS.push(exp[i]);
                i++; //处理完当前运算符才++
            }
            //栈顶优先, 出栈计算
            else if (ret > 0){
                oS.pop();
                int num2 = nS.top(); nS.pop();
                int num1 = nS.top(); nS.pop();
                nS.push(op2num(op1, num1, num2));
                //处理栈顶运算符, 当前运算符没处理不++
            }
            //优先级相等(左右括号), 弹出
            else {
                oS.pop();
                i++;
            }


        }
    }
    //最终结果会在栈顶(栈只有一个元素)
    cout << nS.top() << endl;
}

前缀, 后缀表达式
中缀表达式: 运算符放在两个运算数中间
前缀表达式: 运算符放在两个运算数之前
后缀表达式: 运算符放在两个运算数之后

  1. 前缀表达式
    特点: 运算符在运算数之前
    运算数顺序跟中缀表达式相同
    运算符按照运算顺序的逆序排列
  2. 后缀表达式
    特点: 运算符在运算数之后
    运算数顺序跟中缀表达式相同
    运算符按照运算顺序排列
  3. 中缀表达式->后缀表达式
    运算数顺序不变
    运算数后的运算符的顺序由中缀表达式的计算过程确定
    运算数直接输出
    运算符优先栈顶则入栈, 否则输出

中缀表达式计算需要按照运算符优先级来分情况处理
前/后缀表达式运算顺序已经确定好了, 遇见运算符就计算, 不必反复出入栈, 即只需要一个运算数栈, 不要运算符栈
前后缀表达式计算效率比中缀表达式高, 计算中缀表达式前可以先转换成前/后缀表达式

  1. 程序栈与递归
    保存当前函数的的局部变量
    一个函数调另一个函数时完成三件事
    1. 将所有参数, 返回地址等信息传递给被调用函数
    2. 被调用函数的局部变量"入栈"“程序堆栈”
    3. 将控制转移到被调用函数的入口
      被调用函数执行完后
    4. 保存被调用函数的计算结果
    5. 从"程序堆栈""出栈"释放被调函数的局部变量
    6. 依照被调用函数的保存的返回地址将控制转移到调用函数
      例: 汉诺塔
#include <iostream>

using namespace std;

void move(char a, char b){
    cout << "move " << a << " to " << b << endl; 
}

//x起始y辅助z目标
void hanoi(int n, char x, char y, char z){
    if (n==1) move(x, z);
    else{
        hanoi(n-1, x, z, y);
        move(x, z);
        hanoi(n-1, y, x, z);
    }
}

int main(){
    hanoi(3, 'A', 'B', 'C');
    return 0;
}
发布了7 篇原创文章 · 获赞 2 · 访问量 551
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览