上一讲学习了栈以及顺序栈的实现,本讲我们继续学习另一种栈--链式栈
什么叫链式栈,就是和链表类似,不过它需要遵守栈的特性,只能操作其中一端,我们不需要从头创建一个链式栈,只在单链表的基础上进行实现即可。
链式栈的设计要点:
类模板编程。
继承自抽象父类Stack。
在内部组合使用LinkList类实现链式存储。
只在单链表的其中一端进行操作。
具体实现如下:
template< typename T>
class LinkStack : public Stack<T>
{
protected:
LinkList<T> m_list;
public:
void push(const T& obj)
{
m_list.insert(0,obj);//操作d单链表的第0个节点。
}
void pop()
{
if(m_list.length() > 0)
m_list.remove(0);
else
THROW_EXCEPTION(InvalidOperationException, "mo element to pop...");
}
int size()const//const对象调用
{
return m_list.length();
}
T top()const//const 对象调用
{
if(m_list.length() > 0)
return m_list.get(0);
else
THROW_EXCEPTION(InvalidOperationException, "mo element to top...");
}
void clear()
{
m_list.clear();
}
};
总体看来实现其实比顺序栈更简单,借用了单链表的实现,单链表的实现具体请看单链表那一篇文章。
小算法
既然文章已经开始了,又不能就这么匆匆结束,所以接下来实现一个小算法,具体要求如下:
扫描一串代码,进行左右操作符匹配,返回匹配结果,使用链式栈实现。
算法思路:
一、从第一个字符开始扫描
1、扫描到普通符号时忽略
2、扫描到左操作符时让符号入栈
3、扫描到右操作符时将栈顶符号并与之进行匹配,成功匹配进行出栈操作,失败直接可以返回。
二、扫描结束
成功:所有字符扫描完毕且栈的状态为空。
失败:匹配失败或者扫描完成后栈不为空。
实现
在实现之前需要一些辅助函数,对左右操作符进行判断,由于单引号和双引号左右都一样,需要单独处理,以及实现操作符之间匹配函数。
操作符判定:
bool isLeft(char c)//左操作符
{
return ( c == '(' ) || ( c == '{' ) || ( c == '[' ) || ( c == '<' );
}
bool isRight(char c)//右操作符
{
return ( c == ')' ) || ( c == '}' ) || ( c == ']' ) || ( c == '>' );
}
bool isQuot(char c)//引号
{
return (c == '\'') || (c == '\"');
}
操作符匹配:
bool isMatch(char l, char r)
{
return ( (l == '(') && (r == ')') ) ||
( (l == '{') && (r == '}') ) ||
( (l == '[') && (r == ']') ) ||
( (l == '<') && (r == '>') ) ||
( (l == '\'') && (r == '\'') ) ||
( (l == '\"') && (r == '\"') );
}
bool Scan(const char* str)
{
bool ret = true;
LinkStack<char> stack;
int i = 0;
if(str != NULL)
{
while(ret && str[i] != '\0')
{
if( isLeft(str[i]) )//处理左操作符
{
stack.push(str[i]);
}
else if( isRight(str[i]) )//处理右操作符
{
if( (stack.size() > 0) && isMatch(stack.top(), str[i]) )
{
stack.pop();
}
else
{
ret = false;
}
}
else if( isQuot(str[i]) )//处理引号
{
if( (stack.size() == 0) || (!isMatch(stack.top(), str[i])) )
{
stack.push(str[i]);
}
else if(isMatch(stack.top(),str[i]))
{
stack.pop();
}
}
i++;
}
}
else
{
ret = false;
}
return (ret && stack.size() == 0);
}
遇到左操作符时直接入栈。
遇到右操作符先判断栈是否为空且匹配是否成功,要是成功的话就直接弹出栈顶元素
遇到引号先判断栈是否为空或者匹配是否成功,如果栈为空或者匹配失败就将当前扫描到的引号入栈,如果匹配成功了就将栈顶元素弹出。
然后自增循环变量i扫描下一个字符。
当扫描完毕后判断条件变量和栈的状态是否满足要求,当条件变量为真且栈为空时就返回匹配结果为真,否则就匹配失败,返回假。
总结:
链式栈实现组合使用了单链表类。
在单链表头部进行操作能够高效的入栈出栈的操作。
栈的后进先出特性适用于检测成对出现的符号。
栈非常适合于就近匹配场合
最后的测试代码请大家自行实现,在此处不占用篇幅。
栈的学习到此为止,下一阶段学习队列,在此感谢狄泰唐老师。