抵制转载并改了作者名的流氓行为,博客地址: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)"是匹配的,但是"哈哈:)"就是不匹配的。利用栈,我们把暂时未匹配的前半边括号入栈,当读取到后半边括号且能与栈顶的前半边匹配时,栈顶弹出,完成一个匹配。最好如果栈空,则是匹配的,反之不匹配。代码很简单:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 bool brace_match(const char* str) { 2 Stack<char> s; 3 for(int i = 0; str[i] != '\0'; 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 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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,读取完成后,运算符栈依次全部出栈。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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] != '\0') { 47 char c = str[i++]; 48 if(c >= '0' && c <= '9') { 49 int num = c - '0'; 50 while((c = str[i]) != '\0') { 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 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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更新新的实现。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
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 */