数据结构——对于栈的理解

引入:

在之前的学习中,我们经常听说栈,比如:在对比for循环与递归时,递归可能会导致栈溢出。今天就让我们对栈进行更加深入的探究。

栈的相关定义

栈是限定仅在表尾进行插入或删除操作的线性表。栈是一种操作受限的线性表,它的基本操作是线性表操作的子集,因此又被称为限定性的数据结构

栈顶

栈的表尾端称为栈顶。

栈底

栈的表头端称为栈底。

空栈

不含元素的空表。

栈的特点

栈的特点示意图

栈S = ( a1, a2, a3, …, an) ,则a1为栈底元素,an为栈顶元素。

栈的特点是后进先出——把栈想象成一个一端封闭,一端开口的桶,往桶里放饼,一层一层的放,若想拿出第一个放进去的饼,就需要先把它上面的饼拿出去,这就是后进先出。LIFO

栈的表示

既然说栈是一种特殊的线性表,那么相应的,栈也有两种存储表示方法:顺序栈和链栈。

顺序栈

栈的顺序存储结构是利用一组地址连续的存储单元一次存放自栈底到栈顶的数据元素。

定义

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

顺序栈的定义

typedef int Position;
struct SNode {
    ElementType *Data; /* 存储元素的数组 */
    Position Top;      /* 栈顶指针 */
    int MaxSize;       /* 堆栈最大容量 */
};
typedef struct SNode *Stack;

创建

思路

申请数组最大值 * 结构体大小的空间作为栈的存储空间
当栈为空时,Top值为-1
Top代表的是栈顶元素在数组中位置的下标

CODE

顺序栈的创建

Stack CreateStack( int MaxSize )
{
    Stack S = (Stack)malloc(sizeof(struct SNode));
    S->Data = (ElementType *)malloc(MaxSize * sizeof(ElementType));
    S->Top = -1;
    S->MaxSize = MaxSize;
    return S;
}

注意:当堆栈为空时,Top值为-1.

判满

思路

当Top的大小与数组最大下标相等时表示栈已满

CODE

顺序栈的判满

bool IsFull( Stack S )
{
    return (S->Top == S->MaxSize-1);
}

判空

思路

根据定义,Top值为-1表示空

CODE

顺序栈的判空

bool IsEmpty( Stack S )
{
    return (S->Top == -1);
}

入栈(push)

思路

先判断堆栈满不满,不满则把新元素放入Top + 1的位置

CODE

入栈操作

bool Push( Stack S, ElementType X )
{
    if ( IsFull(S) ) {
        printf("堆栈满");
        return false;
    }
    else {
        S->Data[++(S->Top)] = X;
        return true;
    }
}

出栈(pop)

思路

先判断堆栈空不空,不空则把Top位置元素返回

CODE

入栈操作

ElementType Pop( Stack S )
{
    if ( IsEmpty(S) ) {
        printf("堆栈空");
        return ERROR; /* ERROR是ElementType的特殊值,标志错误 */
    }
    else 
        return ( S->Data[(S->Top)--] );
}

注意:先变化Top还是先进行元素操作?

例:用一个数组实现两个堆栈

1.怎么设计?
2.入栈操作如何实现?
3.出栈操作如何实现?

链栈

栈的链式存储结构实际上就是一个单链表。插入和删除操作只能在链栈的栈顶进行。

栈顶指针

栈顶指针Top应该在链表的哪一头?

定义

链栈的定义

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

创建

思路

构建一个堆栈头结点,其下一个结点指向NULL

CODE

链栈的创建

Stack CreateStack( ) 
{ /* 构建一个堆栈的头结点,返回该结点指针 */
    Stack S;

    S = (Stack)malloc(sizeof(struct SNode));
    S->Next = NULL;
    return S;
}

判空

思路

判断栈是否为空,只需要判断头结点的指向是否为NULL即可。

CODE

链栈的判空

bool IsEmpty ( Stack S )
{ /* 判断堆栈S是否为空,若是返回true;否则返回false */
    return ( S->Next == NULL );
}

入栈(push)

思路

申请一个新节点并赋值,另其指向头结点的下一个结点,头结点指向该结点

CODE

链栈的插入

bool Push( Stack S, ElementType X )
{ /* 将元素X压入堆栈S */
    PtrToSNode TmpCell;

    TmpCell = (PtrToSNode)malloc(sizeof(struct SNode));
    TmpCell->Data = X;
    TmpCell->Next = S->Next;
    S->Next = TmpCell;
    return true;
}

提问:为什么链栈在push操作是不用判断满不满?

出栈(pop)

思路

判别是否是空栈,若不是空栈,则:
1.将待删除结点赋值给一个新的结点
2.将待删除结点的内容赋值给一个新的变量
3.头结点指向待删除结点的下一个结点
4.释放新节点内存
5.返回新变量

CODE

链栈的删除

ElementType Pop( Stack S )  
{ /* 删除并返回堆栈S的栈顶元素 */
    PtrToSNode FirstCell;
    ElementType TopElem;

    if( IsEmpty(S) ) {
        printf("堆栈空"); 
        return ERROR;
    }
    else {
        FirstCell = S->Next; 
        TopElem = FirstCell->Data;
        S->Next = FirstCell->Next;
        free(FirstCell);
        return TopElem;
    }
}

栈的应用举例

函数调用及递归实现

回溯算法

回溯法是算法设计中的一种重要思想。

举个例子:老鼠走迷宫。给定一个迷宫,老鼠从入口进去,从出口出去。在这个过程中没有什么特殊的算法,只能不断的试探,试探下一步可以往哪里走。到了下一步,再去试探接下来的可能性。当到某一步各种可能性都行不通时,就回到上一步的位置再去试探。

这个过程要把试探的路径保存起来,若某一次试探不成功,就要回到最近一次试探的状态。这里就可以利用堆栈的特性

深度优先搜索

表达式求值

表示方式

中缀表达式

运算符位于运算数之间

如: a + b * c - d / e

后缀表达式

运算符位于运算数之后

如: a b c * + d e / -

化为后缀表达式

利用后缀表达式更容易求值,因为在遇到一个运算时它的操作数已知,所以考虑将中缀表达式转化为后缀表达式

不带括号

例: 2 + 9 / 3 - 5 → 2 9 3 + / 5 -

带括号

例: a * ( b + c ) / d → a b c + * d /

思路

1.运算数:直接输出
2.左括号:压入堆栈(在栈外优先级高,在栈内优先级最低)
3.右括号:将栈顶的运算符弹出并输出,直到遇到左括号(出栈、不输出)
4.运算符:
· 若优先级大于栈顶运算符时,则压栈
· 若优先级小于等于栈顶运算符时,将栈顶运算符弹出并输出;再比较新的栈顶运算符,直到该运算符大于栈顶运算符优先级为止,然后将该运算符压栈
5.若各对象处理完毕,则把堆栈中存留的运算符一并输出

后缀表达式求值

例:

6 2 / 3 - 4 2 * + = ?

思路

1.运算数:入栈
2.运算符:从堆栈中弹出适当数量的运算数,计算并结果入栈
3.最后,堆栈顶上的元素就是表达式的结果值

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值