数据结构(二)—— 线性结构(2):堆栈

数据结构系列内容的学习目录 → \rightarrow 浙大版数据结构学习系列内容汇总

2. 堆栈

2.1 什么是堆栈

  计算机如何进行表达式求值?
  例: 算术表达式 5 + 6 / 2 − 3 ∗ 4 5+6/2-3* 4 5+6/234,正确理解为 5 + 6 / 2 − 3 ∗ 4 = 5 + 3 − 3 ∗ 4 = 8 − 3 ∗ 4 = 8 − 12 = − 4 5+6/2-3* 4=5+3-3* 4=8-3* 4=8-12 =-4 5+6/234=5+334=834=812=4
     • 由两类对象构成:
       ⋄ \diamond 运算数,如2、3、4
       ⋄ \diamond 运算符号,如+、-、*、/
     • 不同运算符号优先级不一样
  中缀表达式: 运算符号位于两个运算数之间。如, a + b ∗ c − d / e a+b*c- d/e abcd/e
  后缀表达式: 运算符号位于两个运算数之后。如, a b c ∗ + d e / − a b c*+d e/- abc+de/
  例如 5 + 6 / 2 − 3 ∗ 4 5+6/2-3* 4 5+6/234可以表达为 62 / 3 − 42 ∗ + 62/3-42*+ 62/342
  后缀表达式求值策略:从左向右“扫描”,逐个处理运算数和运算符号。
    1. 遇到运算数怎么办?如何“记住”目前还未参与运算的数?
    2. 遇到运算符号怎么办?对应的运算数是什么?
  启示: 需要有种存储方法,能顺序存储运算数,并在需要时“倒序”输出!

  栈(stack)又名堆栈,它是一种运算受限的线性表,限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
  堆栈是一种数据项按序排列的数据结构,只能在一端(称为栈顶(top))对数据项进行插入和删除。

  堆栈(Stack): 具有一定操作约束的线性表
     ⋄ \diamond 只在一端(栈顶, Top)做插入、删除
     ⋄ \diamond 插入数据:入栈(Push)
     ⋄ \diamond 删除数据:出栈(Pop)
     ⋄ \diamond 后入先出:Last In First Out (LIFO)

2.2 堆栈的抽象数据类型描述

  类型名称: 堆栈(Stack)
  数据对象集: 一个有0个或多个元素的有穷线性表
  操作集: 长度为MaxSize的堆栈S ∈ ∈ Stack,堆栈元素item ∈ ∈ ElementType

  1. Stack CreateStack( int MaxSize ):生成空堆栈,其最大长度为MaxSize;
  2. int IsFull( Stack S, int MaxSize ):判断堆栈s是否已满;
  3. void Push( Stack S, ElementType item ):将元素item压入堆栈;
  4. int IsEmpty ( Stack S ):判断堆栈s是否为空;
  5. ElementType Pop( Stack S ):删除并返回栈顶元素。

在这里插入图片描述
  Push和Pop可以穿插交替进行:
   (1) Push(S,A),Push(S,B),Push((S,C),Pop(S),Pop(S),Pop(S)
     堆栈输出是CBA
   (2) Push(S,A),Pop(S),Push(S,B),Push((S,C),Pop(S),Pop(S)
     堆栈输出是ACB

2.3 堆栈的顺序存储实现

  堆栈的顺序存储结构通常由一个一维数组和一个记录栈顶元素位置的变量组成。

  堆栈的顺序存储数据结构定义如下所示。

#define MaxSize<储存数据元素的最大个数>
typedef struct SNode *Stack;
struct SNode{
    ElementType Data[MaxSize];
    int Top;
}

  (1) 入栈

在这里插入图片描述

void Push(Stack Ptrs,ElementType item)
{
    if ( PtrS->Top == MaxSize-1 ) {
        cout << "堆栈满" << endl;
        return;
    }else {
        PtrS->Data[++(PtrS->Top)] = item;
        return;
}

  (2) 出栈

在这里插入图片描述

ElementType Pop(Stack Ptrs)
{
    if ( PtrS->Top ==-1 ){
        cout << "堆栈空" << endl;
        return ERROR;  //ERROR是ElementType的特殊值,标志错误
    else{
        return ( PtrS->Data[(PtrS->Top)--]);
    }
}

  堆栈的顺序存储实现实例的代码如下所示。

#include<iostream>
using namespace std;
#define MaxSize 100   // 堆栈元素的最大个数 
typedef int ElementType; // ElementType 暂时定义为 int 类型 
typedef struct SNode *Stack;
struct SNode {
	ElementType Data[MaxSize];   // 存储堆栈元素
	int Top;  // 记录栈顶元素下标 
};
Stack S;

Stack CreateStack();  // 初始化堆栈 
int IsFull(Stack S); // 判断堆栈是否已满 
int IsEmpty(Stack S);  // 判断堆栈是否为空 
void Push(Stack S, ElementType item);  // 入栈 
ElementType Pop(Stack S);  // 出栈

// 初始化堆栈 
Stack CreateStack() {
	S = (Stack)malloc(sizeof(struct SNode));
	S->Top = -1;
	return S;
}

// 是否已满 
int IsFull(Stack S) {
	return (S->Top == MaxSize - 1);
}

// 是否为空 
int IsEmpty(Stack S) {
	return (S->Top == -1);
}

// 入栈 
void Push(Stack S, ElementType item) {
	if (IsFull(S)) {   // Top 从 0 开始 
		cout << "堆栈满" << endl;
		return;
	}
	else {
		S->Top++;   // 栈顶元素加一 
		S->Data[S->Top] = item;   // 放进最上 
		return;
	}
}

// 出栈
ElementType Pop(Stack S) {
	if (IsEmpty(S)) {
		cout << "堆栈空" << endl;
		return NULL;
	}
	else {
		ElementType val = S->Data[S->Top];  //取出最上 
		S->Top--;  // 栈顶元素减一 
		return val;
	}
}
int main() {
	S = CreateStack();

	cout << "1入栈" << endl;
	Push(S, 5);

	cout << "2入栈" << endl;
	Push(S, 7);

	cout << "3入栈" << endl;
	Push(S, 66);

	cout << Pop(S) << "出栈" << endl;

	cout << Pop(S) << "出栈" << endl;

	system("pause");

	return 0;
}

  代码运行结果如下图所示。

在这里插入图片描述
  例: 请用一个数组实现两个堆栈,要求最大地利用数组空间,使数组只要有空间入栈操作就可以成功。
  分析: 一种比较聪明的方法是使这两个栈分别从数组的两头开始向中间生长,当两个栈的栈顶指针相遇时,表示两个栈都满了。

  实例的代码如下所示。

#include<iostream>
using namespace std;
#define MaxSize 100   // 堆栈元素的最大个数 
typedef int ElementType; // ElementType 暂时定义为 int 类型 
typedef struct DStack *Stack;
struct DStack {
	ElementType Data[MaxSize];   // 存储堆栈元素
	int Top1; //堆栈1的栈顶指针
	int Top2; //堆栈2的栈顶指针
};
Stack S;

Stack CreateStack();  // 初始化堆栈 
void Push(Stack S, ElementType item, int Tag);  // 入栈 
ElementType Pop(Stack S, int Tag);  // 出栈

// 初始化堆栈 
Stack CreateStack() 
{
	S = (Stack)malloc(sizeof(struct DStack));
	S->Top1 = -1;
	S->Top2 = MaxSize;
	return S;
}

// 入栈 
void Push(Stack S, ElementType item, int Tag)  //Tag作为区分两个堆栈的标志,取值为1和2
{
	if (S->Top2 - S->Top1 == 1) 
	{  
		cout << "堆栈满" << endl;
		return;
	}
	if (Tag == 1)   //对第一个堆栈进行操作
	{
		S->Data[++(S->Top1)] = item;
	}
	else {  //对第二个堆栈进行操作
		S->Data[--(S->Top2)] = item;
	}
}

// 出栈
ElementType Pop(Stack S, int Tag) 
{
	if (Tag == 1)  //对第一个堆栈进行操作
	{
		if (S->Top1 == -1)  //堆栈1为空
		{
			cout << "堆栈1空" << endl;
			return NULL;
		}
		else
			return S->Data[(S->Top1)--];
	}

	if (Tag == 2)  //对第二个堆栈进行操作
	{
		if (S->Top2 == MaxSize)  //堆栈2为空
		{
			cout << "堆栈2空" << endl;
			return NULL;
		}
		else
			return S->Data[(S->Top2)++];
	}
}
int main() {
	S = CreateStack();

	cout << "1入栈1" << endl;
	Push(S, 1, 1);

	cout << "2入栈1" << endl;
	Push(S, 2, 1);

	cout << "3入栈1" << endl;
	Push(S, 3, 1);

	cout << "1入栈2" << endl;
	Push(S, 1, 2);

	cout << "2入栈2" << endl;
	Push(S, 2, 2);

	cout << "3入栈2" << endl;
	Push(S, 3, 2);

	cout << Pop(S,1) << "出栈1" << endl;

	cout << Pop(S,2) << "出栈2" << endl;

	system("pause");

	return 0;
}

  代码运行结果如下图所示。

在这里插入图片描述

2.4 堆栈的链式存储实现

  堆栈的链式存储结构实际上就是一个单链表,叫做链栈。插入和删除操作只能在链栈的栈顶进行。栈顶指针Top应该在链表的哪一头?

  堆栈的链式存储数据结构定义如下所示。

typedef struct SNode *Stack;
struct SNode{
    ElementType Data;
    struct SNode *Next;
};

  (1) 堆栈初始化(建立空栈)
  (2) 判断堆栈s是否为空

在这里插入图片描述

Stack Createstack()
{  //构建一个堆栈的头结点,返回指针
    Stack S ;
    S=(Stack) malloc(sizeof (struct SNode));
    S->Next = NULL;
    return S;
}

int IsErmpty(Stack S)
{ //判断堆栈S是否为空,若为空函数返回整数1,否则返回0
    return ( S->Next == NULL);
}

  (1) 入栈

void Push(ElementType item, Stack S)
{ //将元素item压入堆栈S
    struct SNode *Tmpcell;
    Tmpcell=(struct SNode *) malloc(sizeof(struct SNode));
    Tmpcell->Element = item;
    Tmpcell->Next = S->Next;
    S->Next =TmpCell;
}

  (2) 出栈

ElementType Pop(Stack S)
{ //删除并返回堆栈S的栈顶元素
    struct SNode *Firstcell;
    ElementType TopElem ;
    if(IsEnmpty (S)){
        cout << "堆栈空" << endl;
        return NULL;
    }
    else {
        FirstCell = S->Next;
        S->Next = Firstcell->Next;
        TopElem = Firstcell ->Element;
        free(Firstcell);
        return TopElem;
    }
}

  堆栈的链式存储实现实例的代码如下所示。

#include<iostream>
using namespace std;
typedef int ElementType;
typedef struct SNode *Stack;
struct SNode {
	ElementType Data;
	Stack Next;
};
Stack S;

Stack CreateStack();  // 初始化链栈 
int IsEmpty(Stack S);  // 判断链栈是否为空 
void Push(Stack S, ElementType item);  // 入栈 
ElementType Pop(Stack S);  // 出栈


// 初始化 
Stack CreateStack() {
	S = (Stack)malloc(sizeof(struct SNode));
	S->Next = NULL;
	return S;
}

// 判断是否为空 
int IsEmpty(Stack S) {
	return (S->Next == NULL);
}

// 入栈
void Push(Stack S, ElementType item) {
	Stack tmp;
	tmp = (Stack)malloc(sizeof(struct SNode));
	tmp->Data = item;
	// 链栈栈顶元素是链表头结点,新入栈的链表在栈顶元素后面 
	tmp->Next = S->Next;
	S->Next = tmp;
}

// 出栈
ElementType Pop(Stack S) {
	Stack First;
	ElementType TopVal;
	if (IsEmpty(S)) {
		cout << "堆栈空" << endl;
		return NULL;
	}
	else {
		First = S->Next;   // 出栈第一个元素在栈顶元素后面 
		S->Next = First->Next;  //把第一个元素从链栈删除 
		TopVal = First->Data;   // 取出被删除结点的值 
		free(First);  // 释放空间 
		return TopVal;
	}
}

int main() {
	S = CreateStack();

	cout << "1入栈" << endl;
	Push(S, 1);

	cout << "2入栈" << endl;
	Push(S, 2);

	cout << "3入栈" << endl;
	Push(S, 3);

	cout << Pop(S) << "出栈" << endl;

	cout << Pop(S) << "出栈" << endl;

	system("pause");

	return 0;
}

  代码运行结果如下图所示。

在这里插入图片描述

2.5 堆栈应用:表达式求值

  应用堆栈实现后缀表达式求值的基本过程:1. 从左到右读入后缀表达式的各项(运算符或运算数);
                     2. 运算数:入栈;
                     3. 运算符:从堆栈中弹出适当数量的运算数,计算并结果入栈;
                     4. 最后,堆栈顶上的元素就是表达式的结果值。

在这里插入图片描述
  基本策略: 将中缀表达式转换为后缀表达式,然后求值。
  问题: 如何将中缀表达式转换为后缀?
  方法: 从头到尾读取中缀表达式的每个对象,对不同对象按不同的情况处理。
       1. 运算数:直接输出;
       2. 左括号:压入堆栈;
       3. 右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈,不输出);
       4. 运算符:
        ⋄ \diamond 若优先级大于栈顶运算符时,则把它压栈;
        ⋄ \diamond 若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出,再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈;
       5. 若各对象处理完毕,则把堆栈中存留的运算符一并输出。

  中缀转换为后缀举例: ( 2 ∗ ( 9 + 6 / 3 − 5 ) + 4 ) (2*(9+6/3-5)+4) (2(9+6/35)+4),转化过程如下图所示。

在这里插入图片描述
  堆栈的其他应用:1. 函数调用及递归实现
          2. 深度优先搜索
          3. 回溯算法

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值