数据结构整理(二) —— 栈和队列

抵制转载并改了作者名的流氓行为,博客地址: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 添加
原文地址:https://www.cnblogs.com/wolfred7464/p/4337093.html