zoukankan      html  css  js  c++  java
  • 数据结构整理(二) —— 栈和队列

    抵制转载并改了作者名的流氓行为,博客地址:http://www.cnblogs.com/wolfred7464/

    书接上文,继续线性表。这次写一下栈和队列的简单实现和部分应用。队列的应用比较简单一些,就像现实中排队一样一样的,就不做过多赘述,主要说一下循环队列的实现,即重用已出队的空间。栈的实现也很简单,这里主要说一下栈的2种常见应用——括号匹配,算数表达式求值。

    好了,先说栈吧。栈是一个后进先出的结构,只要保证后进先出,怎么实现都可以,关于代码没太多可说的,下面是一个简单的实现,简单是因为没考虑过多错误处理和动态增长。因为代码量少,所以函数的声明和定义写到一块了:

     1 template <typename T>
     2 class Stack {
     3 private:
     4     int sp;
     5     T* _stack;
     6 public:
     7     Stack(int cap = 1024) {
     8         sp = 0;
     9         _stack = new T[cap];
    10     }
    11     ~Stack() {
    12         delete[] _stack;
    13     }
    14     void push(const T& x) {
    15         _stack[sp++] = x;
    16     }
    17     void pop() {
    18         sp--;
    19     }
    20     const T& top() {
    21         return _stack[sp-1];
    22     }
    23     void clear() {
    24         sp = 0;
    25     }
    26     inline int size() {
    27         return sp;
    28     }
    29 };

    利用后进先出的特点,可以利用栈做到函数的递归调用,线程切换等,不过这是操作系统考虑的问题了。这里说一下2个简单的应用。

    先说括号匹配,括号匹配就是给出一个字符串,判断字符串中的括号是否是匹配的,例如"sin(a+b)"是匹配的,但是"哈哈:)"就是不匹配的。利用栈,我们把暂时未匹配的前半边括号入栈,当读取到后半边括号且能与栈顶的前半边匹配时,栈顶弹出,完成一个匹配。最好如果栈空,则是匹配的,反之不匹配。代码很简单:

     1 bool brace_match(const char* str) {
     2     Stack<char> s;
     3     for(int i = 0; str[i] != ''; i++) {
     4         if(strchr("()[]{}", str[i]) != NULL) {
     5             if(str[i] == ')' && s.size() > 0 && s.top() == '(') {
     6                 s.pop();
     7             } else if(str[i] == ']' && s.size() > 0 && s.top() == '[') {
     8                 s.pop();
     9             } else if(str[i] == '}' && s.size() > 0 && s.top() == '{') {
    10                 s.pop();
    11             } else {
    12                 s.push(str[i]);
    13             }
    14         }
    15     }
    16     return !s.size();
    17 }
    利用栈检测括号匹配
    1 void test_brace_match() {
    2     cout << brace_match("sin(20+10)") << endl;
    3     cout << brace_match("{[}]") << endl;
    4 }
    单元测试

    另一个应用是算数表达式求值,就是给出一个四则运算和小括号组成的算数表达式,按照数学上的优先级计算表达式的结果。就是利用后缀式的做法。我的代码利用2个栈,一个是数字栈,一个是运算符栈,遵循一个简单的规则:

    1,数字入数字栈。

    2,运算符栈空时,运算符入栈。

    3,当前运算符优先级大于栈顶优先级,入栈。

    4,左括号"("入栈。

    5,读到右括号时,运算符栈依次弹出直到遇到左括号。

    6,读取完成后,运算符栈依次全部出栈。

     1 class Evaluation {
     2 private:
     3     Stack<char> s_op;
     4     Stack<int> s_num;
     5     // 比较优先级,判断是否应该入栈
     6     bool op_cmp(char op_sp, char op_cur);
     7     // 计算数字栈顶2个数的结果,结果入栈
     8     void calc(char op);
     9 public:
    10     int evaluate(const char* str);
    11 };
    12 
    13 
    14 bool Evaluation::op_cmp(char op_sp, char op_cur) {
    15     if(op_sp == '(' || op_cur == '(') {
    16         return true;
    17     } else if((op_cur == '*' || op_cur == '/') && (op_sp == '+' || op_sp == '-')) {
    18         return true;
    19     }
    20     return false;
    21 }
    22 
    23 void Evaluation::calc(char op) {
    24     int y = s_num.top();
    25     s_num.pop();
    26     int x = s_num.top();
    27     s_num.pop();
    28     switch(op) {
    29         case '+':
    30             x += y; break;
    31         case '-':
    32             x -= y; break;
    33         case '*':
    34             x *= y; break;
    35         case '/':
    36             x /= y; break;
    37     }
    38     s_num.push(x);
    39 }
    40 
    41 int Evaluation::evaluate(const char* str) {
    42     s_op.clear();
    43     s_num.clear();
    44 
    45     int i = 0;
    46     while(str[i] != '') {
    47         char c = str[i++];
    48         if(c >= '0' && c <= '9') {
    49             int num = c - '0';
    50             while((c = str[i]) != '') {
    51                 if(c >= '0' && c <= '9') {
    52                     num = num * 10 + c - '0';
    53                     i++;
    54                 } else {
    55                     break;
    56                 }
    57             }
    58             s_num.push(num);
    59         } else if(c == ')') {
    60             char op = s_op.top();
    61             s_op.pop();
    62             while(op != '(') {
    63                 calc(op);
    64                 op = s_op.top();
    65                 s_op.pop();
    66             }
    67         } else if(s_op.size() == 0 || op_cmp(s_op.top(), c)) {
    68             s_op.push(c);
    69         } else {
    70             while(s_op.size() > 0 && !op_cmp(s_op.top(), c)) {
    71                 calc(s_op.top());
    72                 s_op.pop();
    73             }
    74             s_op.push(c);
    75         }
    76     }
    77     while(s_op.size() > 0) {
    78         calc(s_op.top());
    79         s_op.pop();
    80     }
    81     return s_num.top();
    82 }
    利用栈进行算数表达式求值
    1 void test_evaluation() {
    2     Evaluation e;
    3     cout << e.evaluate("1+2*3") << endl;
    4     cout << e.evaluate("(1+2)*3") << endl;
    5     cout << e.evaluate("1+2*3+4+5*2") << endl;
    6     cout << e.evaluate("1+2*3+(4+5*2)") << endl;
    7     cout << e.evaluate("1+2*3+((4+5)*2)") << endl;
    8 }
    单元测试

    栈的内容先说这些,接下来是队列。由于队列的实现和应用都比较简单,这里只说一下如何实现循环队列,重用已出队的空间,提高效率。循环队列和一般队列一样,只要保证先进先出,并且保证尾结点的后置结点是头结点就可以,用数组,链表,或者其它方式实现都可以,这里我用循环链表演示一下,为了简化编码,数据类型用int,而没有用template:

    下面第一个的实现是有问题的。2015.9.25更新新的实现。

     1 class Circularqueue {
     2 private:
     3     Node* head;
     4     Node* rear;
     5     int lenth;
     6 public:
     7     Circularqueue(int cap = 128);
     8     ~Circularqueue();
     9     void enqueue(int data);
    10     void dequeue();
    11     int front();
    12     void clear();
    13     inline int size();
    14 };
    15 
    16 Circularqueue::Circularqueue(int cap) {
    17     lenth = 0;
    18     head = new Node();
    19     rear = head;
    20     for(int i = 1; i < cap; i++) {
    21         Node* node = new Node();
    22         rear->next = node;
    23         rear = node;
    24     }
    25     rear->next = head;
    26     rear = head;
    27 }
    28 
    29 Circularqueue::~Circularqueue() {
    30     Node* cur = head->next;
    31     while(cur != nullptr) {
    32         delete head;
    33         head = cur;
    34         cur = cur->next;
    35     }
    36     delete head;
    37 }
    38 
    39 void Circularqueue::enqueue(int data) {
    40     rear->data = data;
    41     rear = rear->next;
    42     lenth++;
    43 }
    44 
    45 void Circularqueue::dequeue() {
    46     head = head->next;
    47     lenth--;
    48 }
    49 
    50 int Circularqueue::front() {
    51     return head->data;
    52 }
    53 
    54 void Circularqueue::clear() {
    55     lenth = 0;
    56     rear = head;
    57 }
    58 
    59 int Circularqueue::size() {
    60     return lenth;
    61 }
    循环队列
      1 #ifndef Circularqueue_hpp
      2 #define Circularqueue_hpp
      3 
      4 #include <cassert>
      5 
      6 template <typename T>
      7 class Node {
      8 public:
      9     Node() {
     10         _next = nullptr;
     11         _last = nullptr;
     12     }
     13     
     14     inline const T& data() {
     15         return _data;
     16     }
     17     
     18     inline Node<T>* next() {
     19         return _next;
     20     }
     21     
     22     inline Node<T>* last() {
     23         return _last;
     24     }
     25     
     26     inline void set_data(const T& data) {
     27         _data = data;
     28     }
     29     
     30     void set_next(Node* next) {
     31         this->_next = next;
     32         next->_last = this;
     33     }
     34     
     35     void set_last(Node* last) {
     36         this->_last = last;
     37         last->_next = this;
     38     }
     39     
     40 private:
     41     T _data;
     42     
     43     Node<T>* _next;
     44     Node<T>* _last;
     45 };
     46 
     47 
     48 template <typename T>
     49 class Circularqueue {
     50 public:
     51     Circularqueue() : Circularqueue(128) {
     52         
     53     }
     54     
     55     Circularqueue(int cap) {
     56         assert(cap > 1 && "cap must > 1");
     57         
     58         _size = 0;
     59         _cap = cap;
     60         
     61         _head = _rear = new Node<T>();
     62         
     63         for(int i = 1; i < cap; i++) {
     64             auto node = new Node<T>();
     65             _rear->set_next(node);
     66             _rear = node;
     67         }
     68         _rear->set_next(_head);
     69         _rear = _head;
     70     }
     71     
     72     ~Circularqueue() {
     73         auto start = _head;
     74         auto cur = _head->next();
     75         
     76         while(cur != start) {
     77             delete _head;
     78             
     79             _head = cur;
     80             cur = cur->next();
     81         }
     82         delete _head;
     83     }
     84     
     85     bool enqueue(const T& data) {
     86         if(this->full()) {
     87             return false;
     88         }
     89         
     90         _rear->set_data(data);
     91         _rear = _rear->next();
     92         _size++;
     93         
     94         return true;
     95     }
     96     
     97     T dequeue() {
     98         T data = _head->data();
     99         
    100         _head = _head->next();
    101         _size--;
    102         
    103         return data;
    104     }
    105     
    106     const T& front() {
    107         return _head->data();
    108     }
    109     
    110     inline bool empty() {
    111         return _size <= 0;
    112     }
    113     
    114     inline bool full() {
    115         return _size >= _cap;
    116     }
    117     
    118     inline int size() {
    119         return _size;
    120     }
    121     
    122     void clear() {
    123         _size = 0;
    124         _rear = _head;
    125     }
    126     
    127 private:
    128     Node<T>* _head;
    129     Node<T>* _rear;
    130     
    131     int _size;
    132     int _cap;
    133 };
    134 
    135 #endif /* CircleQueue_hpp */
    双向循环队列。2015.9.25 添加
  • 相关阅读:
    VUE入门
    搭建内网穿透服务
    nacos集群配置安装
    jenkins入门
    Linux系统管理与自动化运维
    SVN
    JAR包启动
    服务器rm -rf 恢复案例 好文章
    docker入门到放弃
    CentOS7安装图形界面及报错处理
  • 原文地址:https://www.cnblogs.com/wolfred7464/p/4337093.html
Copyright © 2011-2022 走看看