实现数据结构中的栈---后进先出LIFO
作者:互联网
栈是什么?如果用生活中最常见的例子,我想到是书本的放置(当然这是部分人喜欢的做法)。但是给人的感觉是非常直接的,书本整齐跨一叠在一起,放在最上面的那本最先拿走,放在最底下那本书就最后拿走,形象吧,生活中有很多后进先出的栗子,就不一一说明了,用图来说明应该很好理解。
放在最上面的那本书的位置叫栈顶,栈顶是可变的,比如说你拿走了西游记,那么栈顶就是红楼梦得位置。相对应的栈底是不可操作的一端。
栈是一种特殊的线性表,特殊之处在于只能在一端对其进行操作,把元素放到栈中,这操作叫压栈(push),压栈之后,栈顶指针就向上移动。把元素从栈顶弹出,这叫出栈(pop),出栈后栈顶指针向下移动。访问栈只能从栈顶元素进行。栈是很重要的数据结构,在很多算法都有应用,比如图的深度优先遍历算法(DFS),Dijkstra算法的中转站。
栈的实现分为两种,一是顺序栈,用数组来实现,二是链式栈,用指针将结点串联起来,与链表相似,但仅能在栈顶进行操作。
先实现简单的顺序栈,顺序栈简直不要太简单,上代码:
#ifndef STATICSTACK_H
#define STATICSTACK_H
template <typename T, int N>
class StaticStack
{
protected:
T array[N]; // 使用模板参数决定栈的大小
int m_top; // 使用整型作为标记,相当于栈顶指针
int m_size; // 栈中数据元素的个数
public:
StaticStack() // 初始化操作得做一下
{
m_top = -1;
m_size = 0;
}
bool push(const T& obj) // 压栈操作,就是往数组增加一个元素
{
bool ret = m_size < N; // 满了就没办法了
if( ret )
{
array[++m_top] = obj;
++m_size;
}
return ret;
}
bool pop() // 出栈操作
{
bool ret = m_size > 0;
if( ret )
{
--m_top;
--m_size;
}
return ret;
}
T top() // 获取栈顶元素
{
return array[m_top];
}
void clear() // 把栈清空
{
m_top = -1;
m_size = 0;
}
int size() // 获取栈的大小
{
return m_size;
}
~StaticStack()
{
clear();
}
};
#endif
来用一下这个简单顺序栈。记住,后进先出!
#include <iostream>
#include "StaticStack.h"
using namespace std;
int main(int argc, const char* argv[])
{
StaticStack<int, 10> stack;
for(int i = 0; i < 10; ++ i)
{
stack.push(i);
}
// 入栈的时候是 0123456789 的顺序
for(int i = 0; i < 10; ++ i)
{
cout << stack.top() << endl;
stack.pop();
}
return 0;
}
输出正确。OK,顺序栈就可以跳过了,因为顺序栈的实现依赖于原生数组,所以在定义的时候必须给定大小,并且在使用的过程中不能动态改变其大小,这是顺序栈的一个缺点,另外,当顺序栈存放的元素是类类型,那么在定义顺序栈时就会自动调用类的构造函数,有多少个元素就会调用多少次,这样做显然会降低效率,所以顺序栈的使用相对较少。
现在重点来实现链式栈吧,有了顺序栈的基础,实现链式栈也相当容易。
直接上代码:
#ifndef LINKSTACK_H
#define LINKSTACK_H
template <typename T>
class LinkStack
{
protected:
struct Node
{
T data;
Node* next;
};
mutable Node header; // 使用头节点会方便各种操作
int m_size;
public:
LinkStack()
{
header.next = NULL;
m_size = 0;
}
bool push(const T& obj) // 压栈操作
{
bool ret = true;
Node* n = new Node();
if( n != NULL )
{
n->data = obj;
Node* current = &header; // 相当于链表的头插
n->next = current->next;
current->next = n;
++m_size;
}
else
{
ret = false;
}
return ret;
}
bool pop() // 出栈操作
{
bool ret = true;
if( m_size > 0 )
{
Node* current = &header; // 相当于链表的头删
Node* todel = current->next;
current->next = todel->next;
--m_size;
delete todel;
}
else
{
ret = false;
}
return ret;
}
T top() const // 获取栈顶元素
{
Node* current = header->next;
return current->data;
}
int size() const
{
return m_size;
}
void clear() // 清空栈
{
while( m_size > 0 )
{
pop();
}
}
~LinkStack()
{
clear();
}
};
#endif
仅仅是实现一个栈好像没有什么好玩的,顺序栈操作一个数组,链式就几个指针操作。现在用栈来实现一个扫描函数,将字符串中的左右符号进行匹配。举个栗子,“( a { b c [ d < e " f ’ g’ " h > i ] j } k)”,这样一个看起来复杂l凌乱的字符串,怎么知道它的左右符号匹不匹配呢?人工智能嘛,人工来数一下不就可以了吗?Good idea!简单的数几分钟就应该知道结果了,要是一本书那么多的字符串怎么来数?当然是把工作交给计算机!写个程序来检查左右符号是否匹配不就得了。主要注意的地方是左符号、右符号以及引号的处理,其他字符直接忽略。知道重点了,实现就容易了,说干就干,动手!
#include <iostream>
#include "LinkStack.h"
// 需要提前准备相应的辅助函数
bool is_Left(const char c) // 判断是否为左符号
{
return (c == '(') || (c == '{') || (c == '<') || (c == '[');
}
bool is_Right(const char c) // 判断是否为右符号
{
return (c == ')') || (c == '}') || (c == '>') || (c == ']');
}
bool is_Quot(const char c) // 判断是否为引号
{
return (c == '\'') || (c == '\"');
}
bool is_Match(const char l,const char r) // 进行匹配判断
{
bool ret = ( (l == '(') && (r == ')') ) ||
( (l == '{') && (r == '}') ) ||
( (l == '[') && (r == ']') ) ||
( (l == '<') && (r == '>') ) ||
( (l == '\'') && (r == '\'') ) ||
( (l == '\"') && (r == '\"') ) ;
return ret;
}
bool scan_func(const char* str) // 扫描字符串的函数
{
bool ret = true;
int i = 0;
str = str ? str : ""; // 参数合法性判断,如果参数为空,则用空字符串代替
LinkStack<char> stack; // 看着怎么使用栈吧
while( ret && (str[i] != '\0') ) // 循环遍历字符串
{ // 如果有不匹配的字符就立即结束循环
if( is_Left(str[i]) ) // 左符号,直接入栈
{
stack.push(str[i]);
}
else if( is_Right(str[i]) ) // 右符号,判断栈是否为空
{ // 如果栈为空,结束循环
if( (stack.size() > 0) && (is_Match(stack.top(), str[i])) )
{
stack.pop(); // 栈不为空,且与栈顶元素能匹配,那就把栈顶中的左符号出栈
}
else
{
ret = false;
}
}
else if( is_Quot(str[i]) ) // 引号判断
{ // 如果栈为空或者与栈顶元素不匹配,入栈
if( (stack.size() == 0) || (!is_Match(stack.top(), str[i])) )
{
stack.push(str[i]);
} // 与栈顶元素匹配,那就将栈顶的引号出栈
else if( is_Match(stack.top(), str[i]) )
{
stack.pop();
}
}
}
return (ret && (stack.size() == 0) );
}
int main(int argc, const char* argv[])
{
const char* str1 = " < ( [ { 123 } ] ) > ";
const char* str2 = " \" \' 123 \' \" ";
const char* str3 = "< 123 ";
cout << endl;
cout << str1 << " result of match : " << ( scan_func(str1) ? " true " : " false " ) << endl;
cout << endl;
cout << str2 << " result of match : " << ( scan_func(str2) ? " true " : " false " ) << endl;
cout << endl;
cout << str3 << " result of match : " << ( scan_func(str3) ? " true " : " false " ) << endl;
return 0;
}
看看输出结果,多easy,要是用人工的智能来数,得花点点点时间。
标签:const,LIFO,ret,后进先出,bool,str,数据结构,stack,size 来源: https://blog.csdn.net/Dream136/article/details/106816090