实现数据结构中的栈---后进先出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,要是用人工的智能来数,得花点点点时间。

在这里插入图片描述

栏目