文章目录
一.静态栈的缺陷
当存储的元素为类类型的时候,静态栈会的对象在创建的时候会多次调用元素类型的构造函数,影响效率,当使用原生数组作为存储空间,在创建创建栈的时候会调用泛指类型T的构造函数,当函数退出的时候又调用析构函数
1.1 测试代码
#include <iostream>
#include "staticStack.h"
using namespace std;
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
};
int main()
{
staicStack<Test, 5> stack;
cout << stack.size() << endl;
}
结果为:
从结果来看,此时栈中没有任何元素,但是却调用了5次构造函数和析构函数
二.链式栈的实现
实际上就是单链表,定义一个top指针,始终指向链表的首元素,当入栈的时候将新结点的next指针指向top指针,并移动top指针,出栈的直接摧毁首结点即可
2.1 设计要点
- 抽象父类Stack的直接子类
- 在内部组合使用LinkList类,即链表类,实现栈的链式存储
- 只在单链表的头部进行操作
2.2 入栈
单链表的头插法
void push(const T& e)
{
//直接使用头插法插入链表元素
m_list.insert(0, e);
}
2.3 出栈
移除下标为0的元素
void pop()
{
//出栈直接弹出下标为0的结点
if (m_list.length() > 0)
{
m_list.remove(0);
}
else
{
cout << "no node to pop" << endl;
}
}
2.4 获取栈顶元素
T top()const
{
if (m_list.length() > 0)
{
m_list.get(0);
}
else
{
cout << "no node to pop" << endl;
}
}
2.5 清空栈
void clear()
{
m_list.clear();
}
2.6 栈成员个数
int size()
{
return m_list.length();
}
三.完整代码
3.1 LinkList.h
#ifndef __LINK_LIST_
#define __LINK_LIST_
#include <iostream>
using namespace std;
template<class T>
class LinkList
{
public:
struct Node
{
T value;
Node* next;
};
mutable struct
{
char reverse[sizeof(T)];
Node* next;
} m_header;
int m_length;
int m_step;
Node* m_current;
virtual Node* create()
{
return new Node();
}
void destroy(Node* pn)
{
delete pn;
}
public:
LinkList()
{
m_header.next = NULL;
m_length = 0;
m_step = 0;
m_current = NULL;
}
Node* position(int i)const
{
Node* pre = reinterpret_cast<Node*>(&m_header);
for (int pos = 0; pos < i; pos++)
{
pre = pre->next;
}
return pre;
}
int find(const T& e)const
{
Node* pre = reinterpret_cast<Node*>(&m_header);
int i = 0;
while (e != pre->next->value)
{
pre = pre->next;
i++;
}
return i;
}
bool end()
{
return m_current == NULL;
}
bool move(int i, int step = 1)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
m_current = position(i)->next;
m_step = step;
}
return ret;
}
T current()
{
if (!end())
{
return m_current->value;
}
else
{
cout << "current end()" << endl;
return -1;//不知道写啥值
}
}
bool next()
{
int i = 0;
while (!end() && i < m_step)
{
m_current = m_current->next;
i++;
}
return i == m_step;
}
bool insert(int i, const T& e)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
Node* node = create();
if (node != NULL)
{
Node* pre = reinterpret_cast<Node*>(&m_header);
pre = position(i);
node->value = e;
node->next = pre->next;
pre->next = node;
}
else
{
cout << "no memery to malloc" << endl;
}
}
m_length++;
cout << "m_l=" << m_length << endl;
return ret;
}
bool remove(int i)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
Node* pre = reinterpret_cast<Node*>(&m_header);
pre = position(i);
Node* toDel = pre->next;
pre->next = toDel->next;
delete toDel;
m_length--;
}
return ret;
}
bool set(int i, T& e)
{
bool ret = (i >= 0) && (i <= m_length);
if (ret)
{
Node* pre = reinterpret_cast<Node*>(&m_header);
pre = position(i);
pre->next->value = e;
}
return ret;
}
T get(int i) const
{
bool ret = (i >= 0) && (i <= m_length);
T e;
if (ret)
{
Node* pre = reinterpret_cast<Node*>(&m_header);
pre = position(i);
e = pre->next->value;
}
return e;
}
int length() const
{
return m_length;
}
void clear()
{
while (m_header.next != NULL)
{
Node* toDel = m_header.next;
m_header.next = toDel->next;
destroy(toDel);
}
m_length = 0;
}
~LinkList()
{
clear();
}
};
#endif
3.2 Stack.h
#ifndef STACK_H
#define STACK_H
template<class T>
class Stack
{
public:
virtual void push(const T& e) = 0;//入栈
virtual void pop() = 0;//出栈
virtual T top() const = 0;//返回栈顶元素
virtual void clear() = 0;//清空栈
virtual int size() = 0;//返回栈的大小
};
#endif
3.3 linkStack.h
#pragma once
#include "LinkList.h"
#include "Stack.h"
template<class T>
class linkStack :public Stack<T>
{
protected:
LinkList<T> m_list;
public:
void push(const T& e)
{
//直接使用头插法插入链表元素
m_list.insert(0, e);
}
void pop()
{
//出栈直接弹出下标为0的结点
if (m_list.length() > 0)
{
m_list.remove(0);
}
else
{
cout << "no node to pop" << endl;
}
}
T top()const
{
if (m_list.length() > 0)
{
return m_list.get(0);
}
else
{
cout << "no node to pop" << endl;
}
}
void clear()
{
m_list.clear();
}
int size()
{
return m_list.length();
}
};
3.4 测试程序
#include <iostream>
#include "linkStack.h"
using namespace std;
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
};
int main()
{
linkStack<Test> stack;
cout << stack.size() << endl;
}
3.5 结果
从结果看到没有再调用构造函数
四:栈实践-符号检测
编译器是如何实现符号检测的呢,下面的实现思路
4.1 实现思路
- 从第一个字符开始扫描
- 当遇到普通字符则不管,遇到左符号则压栈,遇到右符号则弹出栈顶符号,并与右符号进行匹配
- 成功:所有的字符都扫描,且栈为空
- 失败:匹配失败且所有字符都扫描成功当栈为空
4.2 代码实现
#include <iostream>
#include "linkStack.h"
using namespace std;
bool is_left(char c)
{
return (c == '(') || (c == '{') || (c == '[') || (c == '<');
}
bool is_rigth(char c)
{
return (c == ')') || (c == '}') || (c == ']') || (c == '>');
}
bool is_quot(char c)
{
return (c == '\'') || (c == '\"');
}
bool is_match(char l, char r)
{
return ((l == '(') && (r == ')')) ||
((l == '{') && (r == '}')) ||
((l == '[') && (r == ']')) ||
((l == '<') && (r == '>')) ||
((l == '\'') && (r == '\'')) ||
((l == '\"') && (r == '\"'));
}
bool scan(const char* code)
{
linkStack<char> stack;
bool ret = true;
int i = 0;
code = (code == NULL) ? "" : code;
while (code[i] != '\0')
{
//左符号则入栈
if (is_left(code[i]))
{
stack.push(code[i]);
}
else if (is_rigth(code[i]))
{
//遇见右符号,则弹出栈顶符号与其进行匹配
if ((stack.size() > 0 && is_match(stack.top(), code[i])))
{
stack.pop();
}
else
{
ret = false;
}
}
else if (is_quot(code[i]))
{
//如果栈是空,或者当前的引号是左字符(也就是和栈顶不匹配),则入栈
if ((stack.size() == 0) || (!is_match(stack.top(),code[i])))
{
stack.push(code[i]);
}
else if(is_match(stack.top(), code[i]))
{
//匹配上了
stack.pop();
}
}
i++;
}
return ret && (stack.size() == 0);
}
int main()
{
cout << scan("<{a}{b(\'x\')c}d>") << endl;
return 0;
}
结果:
五.小结
- 栈后进先出的特性适合检测成对出现的符号
- 栈适合就近匹配的场合