学习笔记——栈(stack)

目录

1. 栈的实现

1.1 用数组(Array)实现栈

1.2 用链表(linked list)实现栈

3. 栈的应用:检查括号匹配性

4. 中缀(Infix),前缀(Prefix),后缀(Postfix)

4.1 解析后缀表达式:

4.2 中缀转化为后缀:


Stack ADT——Last - In - First - Out (LIFO)

定义:插入删除操作只能从<栈顶>进行。
栈即为一段封闭的管子:

操作:
(1) Push(x);
(2) Pop();
(3) Top();
(4) IsEmpty();
以上4种操作的时间复杂度都是O(1),即执行的完成时间为常数。

1. 栈的实现

1.1 用数组(Array)实现栈

int A[10];

top = -1;  // empty stack

Push(x)

{

    top=top+1;

    A[top]=x;

}

Pop()

{

    top=top-1;  //不用考虑删除,cause:1) 不属于栈内;2) 在执行Push(x)时自会覆盖原值

}

但数组长度有限,Push时溢出(overflow):
创建一个两倍的数据,并复制原有数据——时间复杂度O(n),即和栈上的元素个数n成正比。Push:1) 不溢出——O(1)
            2) 溢出——O(n)

Top()

{

    return A[top];

}

IsEmpty()

{

    if ( top==-1)

         return true;

    else

        return false;

}

1.2 用链表(linked list)实现栈

插入/删除:1) 在链表尾部——时间复杂度为O(n),即与链表长度成正比;
                   2) 在链表开头——时间复杂度为O(1),即常数;
从时间复杂度上考虑,选择在链表开头进行 插入/删除 操作。

 代码实现如下:

#include <iostream>
using namespace std;

struct Node
{
	int data;
	Node* next;
};

Node* top = NULL;

void Push(int x)
{
	Node* newnode = new Node;
	newnode->data = x;
	newnode->next = NULL;
	if (top == NULL)
		top = newnode;
	else
	{
		newnode->next = top;
		top = newnode;
	}
}

void Pop()
{
	Node* temp = top;
	top = top->next;
	delete temp;
}

void Print()
{
	cout << "Stack : ";
	Node* temp = top;
	while (temp != NULL)
	{
		cout << temp->data << '\t';
		temp = temp->next;
	}
	cout << endl;
}

int main()
{
	Push(2); Print();
	Push(5); Print();
	Push(6); Print();
	Pop(); Print();
}

2. 栈的应用:检查括号匹配性

伪代码如下:

for ( i <- 0 to n-1 )

{

    if ( exp[i] is '(' or '[' or '{' )

        push(exp[i]);

    else if ( exp[i] is '(' or '[' or '{' )

    {

        if ( stack is empty ) || ( top doesn't pair with exp[i] )

             {

                   return false;

             }

        else

             pop();

     }

}

//检查括号匹配性
#include <iostream>
#include<stdio.h>
#include<string>
#include<stack>
using namespace std;

bool Arepair(char a,char b)
{
	if (a == '(' && b == ')') return true;
	else if(a == '[' && b == ']') return true;
	else if (a == '{' && b == '}') return true;
	else return false;
}

bool AreBalance(string exp,int n)
{
	stack<char> S;
	for (int i = 0; i < n; i++)
	{
		if (exp[i] == '(' || exp[i] == '[' || exp[i] == '{')
			S.push(exp[i]);
		else if (exp[i] == ')' || exp[i] == '}' || exp[i] == ']')
		{
			if (S.empty() || !Arepair(S.top(), exp[i]))
				return false;
			else	
				S.pop();
		}
	}
	if (S.empty())
		return true;
	else
		return false;  //return S.empty() ? true:false;
}

int main()
{
	string exp;   //string类
	cout << "Input an expression:";
	cin >> exp;
	int n = exp.length();
	if (AreBalance(exp,n))
		cout << "Balance\n";
	else
		cout << "Not Balance\n";
}

3. 中缀(Infix),前缀(Prefix),后缀(Postfix)

InfixPrefixPostfix
2+3+2 32 3+
p-q-q pq p-
a+b*c+a*bcabc*+
human-readablegood for machines

中缀(Infix): <operand> <operator> <operand> 

后缀(Postfix):  <operand> <operand> <operator>
后缀:最容易解析,求值时间和内存代价最小的

3.1 解析后缀表达式:

Q:如何解析后缀表达式"2 3*5 4*+9-"?
A:从左至右,<操作数>入栈,直至遇上<操作符>,选择栈顶的两个数进行操作,所得<操作数>入栈,直至遍历,栈顶即为所解。
伪代码如下:

Evaluate_Postfix(exp)

{

    stack S;

    for ( i = 0 to length(exp)-1 )

    {

         if ( exp[i] is <operand> )

              push(exp[i]);

         else if ( exp[i] is <operator>)

              {

                      op2 = pop();  //第一个为操作数2,若为前缀: op1 = pop();

                      op1 = pop();  //第一个为操作数1,若为前缀: op2 = pop();

                       result = perform( exp[i], op1 ,op2 )

                       push(result);

               }

      }

      return top;

}

 代码如下:

// 计算后缀表达式的代码
#include <iostream>
#include <stack>  //C++自带的stack库
#include <string>
using namespace std;

//2 3 * 5 4 * + 9 -

stack<int> S;

int PerformOperation(char oper);
int EvaluatePost(string exp);
int GetNum(char* num,int* i);

int main()
{
	string exp;
	cout << "Input an expression:";
	getline(cin, exp);
	int val = EvaluatePost(exp);
	cout << "\nOutput is:"<<val;
}

int EvaluatePost(string exp)
{
	for (int i = 0; i < exp.length(); i++)
	{
		if (exp[i] == ' ' || exp[i] == ',') continue;
		else if (exp[i] == '+' || exp[i] == '-' || exp[i] == '*' || exp[i] == '/')
		{
			int temp = PerformOperation(exp[i]);
			S.push(temp);
		}
		else
		{
			int num = GetNum(&exp[i],&i);
			S.push(num);
		}			
	}
	return S.top();
}

int PerformOperation(char oper)
{
	int op2 = S.top();
	S.pop();
	int op1 = S.top();
	S.pop();   //
	if (oper == '+') return op1 + op2;
	else if (oper == '-') return op1 - op2;
	else if (oper == '*') return op1 * op2;
	else if (oper == '/') return op1 / op2;
}

int GetNum(char* num,int* i)
{
	int val = 0;
	while (!(*num == ' ' || *num == ',')&&!((*num == '+' || *num == '-' || *num == '*' || *num == '/')))
	{
		val = val*10 + *num - '0';
		num++;
		*i= *i+1;
	}
	*i = *i - 1;
	return val;
}

前缀(Prefix): <operator> <operand> <operand> 
Q:如何解析前缀表达式"-+*2 3*5 4 9"?
A:从右至左,<操作数>入栈,直至遇上<操作符>,选择栈顶的两个数进行操作,所得<操作数>入栈,直至遍历,栈顶即为所解。

3.2 中缀转化为后缀:

Q1:如何将中缀表达式"A+B*C-D*E"转化为后缀表达式?
A1:观察到,中缀 —》 后缀,<操作数>顺序不变,仅有<操作符>位置改变。可将<操作符>入栈待用,但如何确定<操作符>出栈时机?
       从左至右,<操作符>入栈,直至遇上一个更低优先级的<操作符>,所有<操作符>出栈,从而清空栈(此处为所有<操作符>是因为,前面的运算操作均已完成),直至到达表达式结尾。
总结<操作符>出栈时机:a) 遇上一个更低优先级的<操作符>;b) 到达表达式结尾。
伪代码如下:

InfixToPostfix(exp)

{

    stack S;

    string result;  //存储转化结果

    for ( i = 0 to length(exp)-1 )

    {

         if ( exp[i] is <operand> )

              result = result + exp[i];

         else if ( exp[i] is <operator>)

              {

                      while (!S.empty() && HigherPrec(S.top(), exp[i]));  //栈顶是否有更高优先级

                       {

                                 result = result + S.top();

                                 S.pop();

                        }

                        S.push( exp[i] )

                }

     }

     while (!S.empty())

     {

              result = result + S.top();

              S.pop();

      } 

      return result;   

}

Q2:如何将带括号的中缀表达式"[(A+B)*C-D]*(E+1*2)"转化为后缀表达式?
A2:括号其实就是一个限制域,只作用于<左括号>到<右括号>,将<左括号>入栈,直至遇上一个<右括号>,这说明这个限制域结束,即为:<操作符>出栈时机b)——到达表达式结尾。
        从左至右,<操作符>入栈,直至遇上一个<右括号>,包括<左括号>在内其前的所有<操作符>出栈,直至到达表达式结尾。
伪代码修改如下:

InfixToPostfix(exp)

{

    stack S;

    string result;  //存储转化结果

    for ( i = 0 to length(exp)-1 )

    {

         if ( exp[i] is <operand> )

              result = result + exp[i];

         else if ( exp[i] is <operator>)

              {

                      while ( !S.empty() && HigherPrec( S.top(), exp[i] )
                  && !IsOpeningParentheses(exp[i]) );  //遇到<左括号>则停下,跳出

                       {

                                 result = result + S.top();

                                 S.pop();

                        }

                        S.push( exp[i] )

                }
         else if ( IsOpeningParentheses(exp[i]) )   //<左括号> '(' '[' '{',压栈

               S.push( exp[i] );
        else if ( IsClosingParentheses(exp[i]) )     //
<右括号> ')' ']' '}',出栈

            {    
                   while ( !S.empty() &&
!IsOpeningParentheses( S.top() )
                       {

                                 result = result + S.top();

                                 S.pop();

                        }

                         S.pop();      //弹出对应的为<左括号>  '(' '[' '{'

     }

     while (!S.empty())

     {

              result = result + S.top();

              S.pop();

      } 

      return result;   

}

代码实现如下:

//实现中缀转后缀的代码
#include<iostream>
#include<stack>
#include<string>

using namespace std;

string InfixToPostfix(string exp);
bool IsOperand(char x);
bool IsOperator(char x);
bool IsOpening(char x);
bool IsClosing(char x);
bool IsHigher(char op1, char op2);
int GetOperatorWeight(char op);

//((A+B)*C-D)*E  
//[(A+B)*C-D]*E
 
int main()
{
	string exp;
	cout << "Enter Infix Expression:";
	getline(cin, exp);
	string postfix = InfixToPostfix(exp);
	cout << "Postfix Expression: " << postfix;
}

string InfixToPostfix(string exp)
{
	stack<char> S;
	string postfix = "";   //置空
	for (int i = 0; i <= exp.length() - 1; i++)   //string类用 .length()
	{
		if (exp[i] == ' ' || exp[i] == ',')   continue; //  有‘’或者‘,’
		else if (IsOperand(exp[i]))
			postfix = postfix + exp[i];
		else if (IsOperator(exp[i]))  //+ - * /
		{
			while (!S.empty() && IsHigher(S.top(), exp[i]))   //栈顶优先级高则出栈
			{
				postfix = postfix + S.top();
				S.pop();
			}
			S.push(exp[i]);
		}
		else if (IsOpening(exp[i]))  //左括号
			S.push(exp[i]);
		else if (IsClosing(exp[i]))   //右括号
		{
			while (!S.empty() && !IsOpening(S.top()))
			{
				postfix = postfix + S.top();
				S.pop();
			}
			S.pop();
		}
	}
	while (!S.empty())
	{
		postfix = postfix + S.top();
		S.pop();
	}
	return postfix;
}


bool IsOperand(char x)
{
	if (x >= '0' && x <= '9') return true;
	if (x >= 'a' && x <= 'z') return true;
	if (x >= 'A' && x <= 'Z') return true;
	return false;  //return false; 不能忘
}
bool IsOperator(char x)
{
	if (x == '+' || x == '-' || x == '*' || x == '/') return true;
	return false;    //return false; 不能忘
}
bool IsOpening(char x)
{
	if (x == '(' || x == '[' || x == '{') return true;
	return false;  //return false; 不能忘
}
bool IsClosing(char x)
{
	if (x == ')' || x == ']' || x == '}') return true;
	return false;  //return false; 不能忘
}


bool IsHigher(char op1, char op2)  //检测op2是否比栈顶低,低/相同则pop
{
	int op1Weight = GetOperatorWeight(op1);
	int op2Weight = GetOperatorWeight(op2);

	// If operators have equal precedence, return true if they are left associative. 
	// return false, if right associative. 
	// if operator is left-associative, left one should be given priority. 
	return op1Weight >= op2Weight ? true : false;
}

// Function to get weight of an operator. An operator with higher weight will have higher precedence. 
int GetOperatorWeight(char op)
{
	int weight = -1;
	switch (op)
	{
	case '+':
	case '-':
		weight = 1;
	case '*':
	case '/':
		weight = 2;
	case ')':
	case ']':
	case '}':
		weight = 3;
	}
	return weight;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值