三、C++实现栈(stack)数据结构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_18108083/article/details/84542371

本文使用C++实现栈数据结构,栈数据结构同之前实现的vector和list数据结构一样都属于线性序列结构,但是栈的数据操作范围仅限于逻辑上的特定顶端,即只能对栈顶进行操作。由于栈结构具有的简洁性和规范性,它既为构建更复杂、更高级数据结构的基础,也是算法设计的基本出发点。鉴于其的基础性及使用的频繁程度,其常常作为标准配置的基本数据结构以硬件直接实现。因此,栈结构无论就工程或是理论而言,其基础性地位都是其他数据结构无法比拟的。

栈结构的特点:后进先出(Last in first out,LIFO),且栈结构及其操作天然地具有递归嵌套性。

一、栈数据结构的实现

栈结构的实现可以通过vector和list数据结构直接封装得到,这里采用C++的继承机制,直接从vector类继承得到stack类,然后对特定的函数进行封装重命名,就可以很方便地实现stack数据结构。(vector.h见之前的博客)

stack接口列表
操作 功能 对象
stack() 默认构造函数  
~stack() 默认析构函数  
empty() 判断栈内部是否为空
size() 返回栈内部元素的个数
push(const T e) 将指定元素入栈
pop() 出栈
top() 返回栈顶元素的引用
#pragma once
#include "vector.h"

template<typename T> 
class stack :public vector<T>
{
public:
	stack() {}
	bool empty() { return (_size) ? false : true; }   //判断是否为空
	int size() { return vector<T>::size(); }
	void push(const T e) { insert(size(), e); }       //压栈
	T pop() { return remove(size() - 1); }            //弹栈
	T& top() { return (*this)[size() - 1]; }          //取顶
};

二、栈数据结构的应用

(a) 操作系统函数调用栈

在windows等大部分的操作系统中,对于每一个二进制应用程序都配有一个调用栈(call stack),借助调用栈可以跟踪进程内的所有函数,记录它们之间的相互调用关系,并保证在每一调用实例执行完毕后,可以准确地返回。

调用栈的基本单位是帧,每当一个函数被调用时,都会相应地创建一帧,记录改函数实例在二进制程序中的返回地址,以及局部变量,传入参数等,并将改帧压如调用栈,这些参数也就相当于保护现场。若在该函数返回之前又发生其他函数的调用,那么同样的会生成一个新的帧并压人调用栈,只有当最后调用的函数返回并从调用栈中弹出,运行控制权才交给之前调用的函数。特别的,栈底肯定是main函数,当所有程序执行完毕,main函数所在的帧也会弹出,这时运行控制权再次回到操作系统。

(b) 将十进制数转换成任意进制数

这里使用栈结构作为容纳程序输出的容器,凭借着其后进先出和容量方面的自适应性,能很好地完成这个任务。

void convert(stack<char>& S, int n, int base)    //缓存栈,待转换10进制数,目标进制基数
{
	if (!n) return;
	char digit[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9','A','B','C','D','E','F' };
	S.push(digit[n%base]);
	convert(S, n / base, base);
}

(c) 编译器检查代码中括号是否匹配正确

括号检查是编译器对代码的语法检查中很重要的一项,借助栈结构后进先出的特点,编写程序对待测代码从左至右进行扫描,将出现的左括号压入栈结构中,只要出现的右括号就进行弹栈操作,通过检查弹出的括号和当前的右括号是不是同一类型(如'('和')','['和']'),即可很方便地实现对代码括号的检查。

bool paren(const char expr[], Rank lo, Rank hi)
{
	stack<char> S;
	while (lo < hi)
	{
		switch (expr[lo++])
		{
		case '{': case'[': case '(':
			S.push(expr[lo]); break;
		case '}':
			if (!S.empty() || (S.pop() != '{')) return false; break;
		case ']':
			if (!S.empty() || (S.pop() != '[')) return false; break;
		case ')':
			if (!S.empty() || (S.pop() != '(')) return false; break;
		default: break;
		}
	}
	return S.empty();
}

其他,由于栈结构后进先出的特点,很适合保存算法的执行路径,所以栈结构在很多问题的求解过程中经常被使用(如,N皇后问题、迷宫问题等)。

猜你喜欢

转载自blog.csdn.net/qq_18108083/article/details/84542371