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 表達式求值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章