1,顺序栈的缺点:
1,顺序栈的存储空间使用的原生数组,但是原生数组作为存储空间的时候,在初始化的时,会调用相应类型的构造函数;这样即使不存储任何元素(m_size = 0),也会使得在定义栈的时候,先构造出 N 个大小为 T 的类型的空间出来,非常浪费效率;
2,代码如下:
1 #include <iostream> 2 #include "StaticStack.h" 3 4 using namespace std; 5 using namespace DTLib; 6 7 class Test : public Object 8 { 9 public: 10 Test() 11 { 12 cout << "Test()" << endl; 13 } 14 15 ~Test() 16 { 17 cout << "~Test()" << endl; 18 } 19 }; 20 21 int main() 22 { 23 StaticStack<int> ls; 24 cout << ls.size() << endl; 25 26 return 0; 27 }
2,链式栈的存储实现:
1,本质是链表;
2,采用和链表一样的实现方式,定义一个 top 指针(链表用指针,数组用下标),始终指向链表的首元素;
3,链式栈的设计要点:
1,类模板,抽象父类 Static 的直接子类;
1,容器,所以要用模板;
2,在内部组合使用 LinkList 类,实现栈的链式存储;
1,组合使用,代码复用;
3,只在单链表成员对象的头部进行操作;
1,不管是入栈还是出栈,仅操作下表为 0 的结点;
4,链式栈的设计要点:
5,编程实现链式栈;
1 #ifndef LINKSTACK_H 2 #define LINKSTACK_H 3 4 #include "Stack.h" 5 #include "LinkList.h" 6 #include "Exception.h" 7 8 namespace DTLib 9 { 10 11 template <typename T> 12 class LinkStack : public Stack<T> 13 { 14 protected: 15 LinkList<T> m_list; // 组合使用单链表类,不用 m_top,因为用单链表的 0 下标时刻就能“指向”表头,也就是栈顶;不用 m_size,因为 m_list 里面有 m_length,可以代表链表长度; 16 public: 17 void push(const T& e) // O(1) 18 { 19 m_list.insert(0, e); 20 } 21 22 void pop() // O(1) 23 { 24 if( m_list.length() > 0 ) 25 { 26 m_list.remove(0); 27 } 28 else 29 { 30 THROW_EXCEPTION(InvalidOperationException, "No element in current LinkStack ..."); 31 } 32 } 33 34 T top() const // O(1) 35 { 36 if( m_list.length() > 0 ) 37 { 38 return m_list.get(0); 39 } 40 else 41 { 42 THROW_EXCEPTION(InvalidOperationException, "No elementin current LinkStack ..."); 43 } 44 } 45 46 void clear() // O(n) 47 { 48 m_list.clear(); 49 } 50 51 int size() const // O(1) 52 { 53 return m_list.length(); 54 } 55 }; 56 57 } 58 59 #endif // LINKSTACK_H
6,栈的应用实践:
1,符号匹配问题:
1,在 C 语言中,有一些成对匹配出现的符号;
1,括号:()/[]/{}/<>
2,引号:''/""(左符号和右符号相同)
2,编译器如何实现符号成对检测?
1,算法思路:
1,从第一个字符开始扫描:
1,当遇见普通字符时忽略;
2,当遇见左括号时压入栈中;
3,当遇见右括号时弹出栈顶符号,并进行匹配;
2,结束:
1,成功:所有字符扫描完毕,且栈为空;
2,失败:匹配失败或所有字符扫描完毕但栈非空;
7,符号匹配的链式检测:
1 #include <iostream> 2 #include "LinkStack.h" 3 4 using namespace std; 5 using namespace DTLib; 6 7 bool is_left(char c) 8 { 9 return (c == '(') || (c == '[') || (c == '{') || (c == '<'); // 字符本质是指针, 字符串本质是变量 10 } 11 12 bool is_right(char c) 13 { 14 return (c == ')') || (c == ']') || (c == '}') || (c == '>'); 15 } 16 17 bool is_quot(char c) 18 { 19 return (c == '\'') || (c == '\"'); 20 } 21 22 bool is_match(char l, char r) 23 { 24 return ( (l == '(') && (r == ')') ) || 25 ( (l == '{') && (r == '}') ) || 26 ( (l == '[') && (r == ']') ) || 27 ( (l == '<') && (r == '>') ) || 28 ( (l == '\'') && (r == '\'') ) || 29 ( (l == '\"') && (r == '\"') ); 30 } 31 32 bool scan(const char* code) 33 { 34 LinkStack<char> stack; 35 int i = 0; 36 bool ret = true; 37 code = (code == NULL) ? " " : code; // 判断参数是否为空,为什么为空了就要赋值为 “ ”, 字符串数组的真是属性到底是什么? 38 39 while( ret && (code[i] != '\0')) // 以字符访问字符串 40 { 41 if( is_left(code[i]) ) 42 { 43 stack.push(code[i]); 44 } 45 else if( is_right(code[i]) ) 46 { 47 if( (stack.size() > 0) && is_match(stack.top(), code[i]) ) 48 { 49 stack.pop(); 50 } 51 else 52 { 53 ret = false; // 这里通过“真假”判断 54 } 55 } 56 else if( is_quot(code[i]) ) 57 { 58 if( (stack.size() == 0) || !is_match(stack.top(), code[i]) ) 59 { 60 stack.push(code[i]); // 这里通过栈“空否”可以判断 61 } 62 else if( is_match(stack.top(), code[i]) ) 63 { 64 stack.pop(); 65 } 66 } 67 i++; 68 } 69 70 return ret && (stack.size() == 0); // “真假”是为了匹配前四种、 而“空否”是为了后两种 71 } 72 73 int main() 74 { 75 cout << scan() << endl; 76 77 return 0; 78 }
8,小结:
1,链式栈的实现组合使用了单链表对象;
2,在单链表的头部进行操作能够实现高效的入栈和出栈的操作;
3,栈“后进先出”的特性适用于检测成对出现的符号;
4,栈非常适合于需要“就近匹配”的场合;