数据结构与算法-栈和队列-链栈(Linked Stack)

2 栈和队列(Stack and Queue)

2.2 链栈(Linked Stack)

链栈是指采用链式存储结构实现的栈。通常链栈用单链表来表示。

在这里插入图片描述

基础代码:

// 声明一些常量
#define OK 1
#define ERROR 0
#define OVERFLOW -2
#define TRUE 1
#define FALSE 0

// Status 是函数返回值类型, 其值是函数结果状态代码。
typedef int Status;
// Boolean 定义布尔型,值就是 TRUE 和 FALSE。
typedef int Boolean;

栈元素结构和栈结构:

// 栈元素类型定义
typedef struct
{
    int data; // 栈元素的数据
} SElemType;

// 链栈的存储结构
typedef struct StackNode
{
    SElemType data;
    struct StackNode *next;
} StackNode, *LinkStack;

在编写代码过程中,经常需要查看栈内的内容,因此需要一个方法可以遍历栈,并打印其中的内容:

// 遍历链栈
void TraverseStack(LinkStack S)
{
    StackNode *current = S;
    while (current != NULL)
    {
        printf("%d ", current->data.data); // 打印栈元素数据
        current = current->next;           // 移动到下一个结点
    }
    printf("\n");
}

2.2.1 初始化

链栈的初始化操作就是构造一个空栈, 因为没必要设头结点, 所以直接将栈顶指针置空即可。

// 初始化链栈
Status InitStack(LinkStack *S)
{
    *S = NULL; // 初始化链栈为空
    return OK;
}

2.2.2 入栈

和顺序栈的入栈操作不同的是, 链栈在入栈前不需要判断栈是否满,只需要为入栈元素动态分配一个结点空间。

在这里插入图片描述

【算法步骤】

  1. 为入栈元素e分配空间,用指针newNode指问。
  2. 将新结点数据域置为 e。
  3. 将新结点插人栈顶。
  4. 修改栈顶指针为 newNode。

【代码实现】

// 入栈操作
Status Push(LinkStack *S, SElemType e)
{
    StackNode *newNode = (StackNode *)malloc(sizeof(StackNode));
    if (!newNode) // 分配失败
        return OVERFLOW;
    newNode->data = e;  // 设置新结点的数据
    newNode->next = *S; // 新结点的next指针指向当前栈顶
    *S = newNode;       // 更新栈顶指针
    return OK;
}

2.2.3 出栈

和顺序栈一样, 链栈在出栈前也需要判断栈是否为空, 不同的是, 链栈在出栈后需要释放出栈元素的栈顶空间。
在这里插入图片描述

【算法步骤】

  1. 判断栈是否为空,若空则返回 ERROR。
  2. 将栈顶元素赋给 e。
  3. 临时保存栈顶元素的空间,以备释放。
  4. 修改栈顶指针,指向新的栈顶元素。
  5. 释放原栈顶元素的空间。

【代码实现】

// 出栈操作
Status Pop(LinkStack *S, SElemType *e)
{
    if (*S == NULL) // 栈空
        return ERROR;
    *e = (*S)->data;      // 将栈顶元素赋值给 e
    StackNode *temp = *S; // 临时指针保存当前栈顶结点
    *S = temp->next;      // 更新栈顶指针
    free(temp);           // 释放原栈顶结点内存
    return OK;
}

2.2.4 获取栈顶元素

与顺序栈一样, 当栈非空时, 此操作返回当前栈顶元素的值, 栈顶指针S保持不变。

// 获取栈顶元素
Status GetTop(LinkStack S, SElemType *e)
{
    if (S == NULL) // 栈空
        return ERROR;
    *e = S->data; // 获取栈顶元素
    return OK;
}

2.2.5 销毁栈

因为栈内的元素是动态申请的,所以销毁栈需要遍历每一个栈内元素,释放其内存。

// 销毁链栈
Status DestroyStack(LinkStack *S)
{
    StackNode *current = *S;
    StackNode *temp;
    while (current != NULL)
    {
        temp = current;          // 保存当前结点
        current = current->next; // 移动到下一个结点
        free(temp);              // 释放当前结点内存
    }
    *S = NULL; // 将栈顶指针置为 NULL
    return OK;
}

2.2.6 判断栈是否为空

// 判断链栈是否为空
Boolean IsEmpty(LinkStack S)
{
    return S == NULL; // 如果栈顶指针为 NULL,则栈为空
}

2.2.7 获取栈的大小

和顺序栈固定大小不同,链栈需要遍历每一个元素,然后进行累加。

// 获取链栈的大小
int StackSize(LinkStack S)
{
    int size = 0;
    StackNode *current = S;
    while (current != NULL)
    {
        size++;                  // 计数器加一
        current = current->next; // 移动到下一个结点
    }
    return size; // 返回栈的大小
}

时间复杂度是 O(n)

2.2.8 清空栈操作

清空链栈需要遍历栈内每一个元素,然后释放其内存,最后将头指针置为NULL。从代码上看和销毁栈的操作是一样的,但从业务含义上看是不一样的。

// 清空链栈
Status ClearStack(LinkStack *S)
{
    StackNode *current = *S;
    StackNode *temp;
    while (current != NULL)
    {
        temp = current;          // 保存当前结点
        current = current->next; // 移动到下一个结点
        free(temp);              // 释放当前结点内存
    }
    *S = NULL; // 将栈顶指针置为 NULL
    return OK;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

晴空闲雲

感谢家人们的投喂

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值