Stack--栈

       

       最近复习到栈。这是一种小巧有用的数据结构,是功能特化的链表。一句话概括:LIFO(last in first out)

       

基本概念:


       实现方式:1)链式实现 2)顺序实现。

       链表的 "addLast" 和 " removeLast" 操作相当于栈的 "push" 和 "pop"操作。


1)链式实现的话,各节点占用的内存空间不一定连续,我前面的一篇博文:"LinkedList--链表" 的代码中,"addLast" 和 " removeLast" 函数就相当于栈的 "push" 和 "pop"函数,这里就不赘述了。


2)顺序实现的话,各节点占用的内存空间连续,下面的参考代码我用静态数组来实现。


(注:C/C++中的malloc/new既可以用于链式实现申请节点空间,也可以用于顺序实现申请节点空间。只不过,链式实现的时候,一次只申请一个节点空间(下次申请的节点空间就可能连不上了)顺序实现的时候,一次申请一段连续的节点空间。另外,不用malloc/new,直接使用静态数组的话,其效果相当于申请了一段连续的内存空间


栈的实现:

#ifndef STACK_H_
#define STACK_H_

#include <stdexcept>
#include <iostream>
using namespace std;

const int MAXN = 100;

template<typename T>
class Stack
{
private:
	T array[MAXN];
	int rear;
public:
	Stack();
	Stack(T list[], int len);
	void push_back(T element);
	T pop_back();
	T top();
	int getSize();
	void print();
};

template<typename T>
Stack<T>::Stack() {
	rear = 0;
}

template<typename T>
Stack<T>::Stack(T list[] , int len) {
	rear = 0;
	if(len <= 1 || len >= MAXN)
	{
		throw runtime_error("Index out of range");
	}
	for(int i = 0 ; i < len ; i ++)
		push_back(list[i]);
}

template<typename T>
void Stack<T>::push_back(T element) {
	array[rear++] = element;
}

template <typename T>
T Stack<T>::pop_back() {
	if(rear == 0)
		throw runtime_error("Index out of range");
	return array[--rear-1];
}

template <typename T>
T Stack<T>::top() {
	if(rear == 0)
		throw runtime_error("Index out of range");
	return array[rear-1];
}

template <typename T>
int Stack<T>::getSize() {
	return rear;
}

template <typename T>
void Stack<T>::print() {
	if(getSize() == 0)
	{
		cout << "Empty stack\n";
		return;
	}
	int i = 0;
	while(i<rear)
		cout << array[i++] << " ";
	cout << endl;
}

#endif


下面是栈的几个简单应用的代码:1. 括号匹配、2. 表达式求值


栈的应用:


括号匹配

/**************************************
 *
 * copyright:   ace_yom (Peizhen Zhang)
 * author:      ace_yom (Peizhen Zhang)
 * date:        2015-8-18
 * description: 链表实现
 * 
 **************************************/
#include <iostream>
#include <stack>
using namespace std;

stack<char> st;
string str;

int main() {
	int i = 0;
	cin >> str;
	while(i < str.length())
	{
		if(st.empty())
		{
			st.push(str[i]);
		}
		else
		{
			char ch = st.top();
			if((ch == '[' && str[i] == ']') || (ch == '(' && str[i] == ')'))
				st.pop();
			else
				st.push(str[i]);
		}
		if(!st.empty() && (st.top()==')'|| st.top()==']'))
			break;
		++ i;
	}
	cout << (st.empty() ? "Yes" : "No") << endl;
	return 0;
}


表达式求值

       表达式求值有两种问题,一种是后缀表达式求值,单栈就可以操作;一种为中序表达式求值,需要使用 “算法优先分析法” 来确定运算符的优先级,并且需要双栈。下面仅介绍第一种--后缀表达式求值。第二种问题--中序表达式求值,读者可以参考文献[1]。


后缀表达式,又称逆波兰表达式。逆波兰表达式可以表示出所有的表达式,且本身不带括号:

如 a-b-c,其逆波兰表达式为:ab-c-;((a+b)*c - d/e) -f,其逆波兰表达式为:ab+c*de/-f-


给定一个输入序列(初始化为后缀表达式,一个指针指向输入序列的),和栈(初始化为空), '@' 为取负号操作,它是一个单目运算符,用于本身就是负数的情况,和双目运算符 '-' 减号分开来。


算法如下所示:

1. 若输入序列的指针指向一个数字(变量名也表示一个数字),则将它压入栈。

2. 若输入序列的指针指向一个操作符:

    1. 若操作符为 ‘@’,则取出栈顶的数字,取反,再压栈。输入序列的指针往后移。

    2. 若操作符不是 '@',则取出栈顶的两个数字,做这个操作,然后把结果压入栈。输入序列的指针往后移。


用栈求后缀表达式:

/***********************************
 *
 * copyright:    ace_yom (Peizhen Zhang)
 * author:       ace_yom (Peizhen Zhang)
 * date:         2015-8-20
 * description:  栈求后缀表达式的值
 *
 **********************************/
#include <iostream>
#include <string>
#include <stack>
using namespace std;

string str;
stack<int> st;

//算法仅支持一位整数,因为重点在于“用栈求后缀表达式的值”
//至于多位整数、浮点数的情况,需要作一些小处理,读者可自行解决

int main() {
	cin >> str;
	bool tag = true;
	if(str[0] < '0' || str[0] > '9')
		tag = false;	
	else
	{
		int i = 0;
		int tmp1,tmp2;
		while(i<str.length())
		{
			char ch = str[i];
			if(ch >= '0' && ch <= '9')
			{
				st.push(int(str[i]-'0'));
				cout << int(str[i]-'0') << endl;
			}
			else
			{
				switch (ch)
				{
					case '@':
						tmp1 = st.top();
						st.pop();
						st.push(-1*tmp1);
						cout << "middle result: " << -1*tmp2<< endl;
						break;
					case '-':
						tmp1 = st.top();
						st.pop();
						tmp2 = st.top();
						st.pop();
						st.push(tmp2-tmp1);
						break;
					case '+':
						tmp1 = st.top();
						st.pop();
						tmp2 = st.top();
						st.pop();
						st.push(tmp2+tmp1);
						break;
					case '*':
						tmp1 = st.top();
						st.pop();
						tmp2 = st.top();
						st.pop();
						st.push(tmp2*tmp1);
						break;
					case '/':
						tmp1 = st.top();
						st.pop();
						tmp2 = st.top();
						st.pop();
						st.push(tmp2/tmp1);
						break;
					default:
						tag = false;
						break;
				}
			}
			i ++;
			if(tag == false)
				break;
		}
	}
	if(!tag)
		cout << "Invalid Expression !\n";
	else
	{
		cout << "The result is " << st.top() << endl;
		st.pop();
	}
	return 0;
}




References:

[1] 数据结构(C 语言版) 严蔚敏 吴伟民 编著--3.2.5 表达式求值

1. 发现错误并改错。给出一个实现插入排序的程序,文件名为InsertionSort.java,该文件中有代码错误,请单步调试,发现错误, 并改正错误,提示:只有一行程序有错误,所以只能修改代码中的某一行程序。要求演示在eclipse开发环境下 单步调试程序,如果没有eclipse开发环境,也可以使用其它集成开发环境,但是必须演示调试能力。 2. 请编写程序输出13/17小数点后第100位的数字是几?考察循环和除法运算以及模运算(求余数运算)。 请补齐FractionalDigit.java文件中的代码 3. 请编写程序,分别使用循环和递归两种方法计算斐波那契数列第9项是几? 斐波那契数列的第1项为1,第2项为1,后续任意项的值为其前两项的和。 请补齐Fibo.java文件中的代码 4. 实现Array类中的indexOf函数; 阅读Node.java和SingleLinkedList.java两个关于单链表的类, 实现SingleLinkedList类中的indexOf函数; 5. 阅读Node.java和SingleLinkedList.java类,利用它们来实现和双向队列功能,请阅读Stack.java 和Deque.java(双向队列),请实现Stack类中的pop函数,请实现Deque.java中的removeLast函数。 6. 利用前面的Stack.java和Deque.java类,实现下面问题的求解: 队列中有从1到7(由小到大排列)的7个整数,问经过一个整数后,出的所有排列数有多少? 如果整数的容量是4(最多能容纳4个整数),那么出的排列数又是多少?(百度查询 卡特兰数) 请补齐Catalan.java文件中的代码 7. 八皇后问题。在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、 同一列或同一斜线上,问有多少种摆法。该程序代码在EightQueens.java文件中,请将void place(int n)函数 中未完成的代码补齐,并写出运行结果。 禁止修改其他代码。 8. 学生排序。给出4名同学,请按照学生的年龄排序,然后输出学生的学号和姓名。具体代码见Student.java文件, 请实现sort函数。并运行该程序。 9. 二叉排序树检索。BstNode.java和BinarySortTree.java是关于二叉排序树的程序文件,请阅读这两个程序 文件,请实现contains函数,并运行该程序。 10. BASE64编码转换问题。请百度百科查询base64编码,实现将二进制字节数据编码转换为base64的字符串数据,以及 将base64字符串转换回二进制字节数据。禁止使用JDK自带的Base64转换程序以及Apache开源网站上的程序, 必须自己编写代码来实现BASE64编码和解码函数。可以为该类添加其他数据成员和函数成员。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值