自学内容网 自学内容网

数据结构第4问:什么是栈?

栈的特点

“栈”这个名字来源于它的形象比喻和数据结构的特点。

栈(Stack)在英文中原意是“堆叠”或者“一摞东西堆起来”,就像我们生活中把盘子或书本一层一层叠放起来一样。这个比喻很形象地反映了栈的数据操作规则:

  • 后进先出(LIFO,last in first out) :最后放上去的元素(最后入栈)会最先被取出(先出栈),就像你从一摞盘子中拿盘子时,最上面的盘子最容易取出。

因此,把这种后进先出操作的线性数据结构称为“栈”,就是基于这种“堆叠”的形象和操作方式的直观描述。

栈其实就是规定了只允许在一端进行插入或删除操作的线性表。其中:

  1. 栈顶(Top):即线性表允许进行插入和删除操作的一端。
  2. 栈底(Bottom):即不允许进行插入和删除操作的另一端。

栈的基本操作

InitStack(&S);// 初始化一个空栈S
StackEmpty(S);// 判断是否为空栈,是空栈则返回true,不是空栈返回false
Push(&S, x);// 入栈,若栈S未满,则将x加入使之成为新栈顶
Pop(&S, x);// 出栈,若栈S非空,则弹出栈顶元素,并用x返回
GetTop(S,&x);// 读栈顶元素,但不出栈,若栈S非空,则用x返回栈顶元素
DestroyStack(&S);// 销毁栈,并释放栈S占用的内存空间

栈的顺序存储结构

采用顺序存储结构的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时设置一个标记(top指针)指示当前栈顶元素的位置。

静态分配内存空间的顺序栈实例

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#define MaxSize 100

typedef struct
{
    int data[MaxSize];    // 栈元素存放空间
    int top;              // 栈顶指针
}stack;

// 初始化一个空栈S
bool initStack(stack *S)
{
    S->top = -1;    // 第一个栈元素是从0开始的,所以栈顶指针为-1
    return true;
}

// push,入栈,若栈S未满,则将x加入使之成为新栈顶
bool push(stack *S, int data)
{
    if(S->top < MaxSize - 1)
    {
        S->data[++S->top] = data;
        return true;
    }
    return false;
}

// pop,出栈,若栈S非空,则弹出栈顶元素,并用x返回
bool pop(stack *S, int *value)
{
    if (S->top == -1)  // 栈空
        return false;
    if (value != NULL)
        *value = S->data[S->top--];
    return true;
}

// 读栈顶元素,但不出栈,若栈S非空,则用x返回栈顶元素
bool GetTop(stack S,int *value)
{
    if (S.top == -1)  // 栈空判断
        return false;
    if (value != NULL)
        *value = S.data[S.top];
    return true;
}

// 销毁栈,并释放栈S占用的内存空间
bool DestroyStack(stack *S)
{
    S->top = -1;
    return true;
}

// 判断栈空或非空,是空栈则返回true,不是空栈返回false
bool StackEmpty(stack *S)
{
    if(S->top == -1)
        return true;
    else
        return false;
}

栈的链式存储结构

用链式存储结构的栈叫链栈,优点是便于多个栈共享存储空间和提高效率,且不存在栈满上溢的情况。通常使用单链表来实现,并且规定所有操作在表头进行。链栈有两种区分:有带节点的和不带头节点的。

动态分配内存空间且不带头节点的链栈实例

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

// 链式结构的栈
typedef struct Linknode
{
    int data;
    struct Linknode *next;
} linkstack;

// 无头节点的链栈初始化
bool init_linkstack_no_head(linkstack **L)
{
    *L = NULL;
    return true;
}

// 入栈,适合无头节点的链栈
bool push_node_no_head(linkstack **L, int data)
{
    linkstack *new_node = malloc(sizeof(linkstack));
    new_node->data = data;
    new_node->next = *L;
    *L = new_node;
    return true;
}

// 出栈,适合无头节点的链栈
bool pop_node_no_head(linkstack **L, int *data)
{
    if (*L == NULL)
        return false; // 空栈,无法弹出
    linkstack *node;
    node = *L;
    *data = (*L)->data;
    (*L) = (*L)->next;
    free(node);
    return true;
}

// 读栈顶元素,但不出栈,若栈S非空,则用data返回栈顶元素
bool GetTop_node(linkstack *L, int *data)
{
    if (L == NULL)
        return false;
    *data = L->data;
    return true;
}

// 判断栈空或非空,是空栈则返回true,不是空栈返回false
bool judge_stack_empty(linkstack *L)
{
    if (L == NULL)
        return true;
    else
        return false;
}

// 销毁栈,并释放栈S占用的内存空间
bool destroy_stack_node(linkstack **L)
{
    if (*L == NULL)
        return false;
    linkstack *current;
    while (*L != NULL)
    {
        current = *L;    // 保存当前节点地址
        *L = (*L)->next; // 栈顶指向下一节点
        free(current);   // 释放当前节点
    }
    *L = NULL;
    return true;
}

动态分配内存空间且带头节点的链栈实例

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

// 有头节点的链栈初始化
bool init_linkstack_have_head(linkstack **L)
{
    *L = malloc(sizeof(linkstack));
    if (*L == NULL)
        return false;
    (*L)->next = NULL;
    return true;
}

// 入栈,适合有头节点的链栈
bool push_node_have_head(linkstack *L, int data)
{
    if (L == NULL)
        return false;
    linkstack *new_node = malloc(sizeof(linkstack));
    if (new_node == NULL)
        return false;
    new_node->data = data;
    new_node->next = L->next;
    L->next = new_node;
    return true;
}

// 出栈,适合有头节点的链栈
bool pop_node_have_head(linkstack *L, int *data)
{
    // 空栈或者栈内只有头节点
    if (L == NULL || L->next == NULL)
        return false; 
    // 创建临时变量存放要弹出的栈元素
    linkstack *node = L->next;
    *data = node->data;
    L->next = node->next;
    free(node);
    return true;
}

// 读栈顶元素,但不出栈,若栈S非空,则用data返回栈顶元素,适合有头节点的链栈
bool GetTop_node_have_head(linkstack *L, int *data)
{
    if (L == NULL || L->next == NULL)
        return false;
    *data = L->next->data;
    return true;
}

// 判断栈空或非空,是空栈则返回true,不是空栈返回false,适合有头节点的链栈
bool judge_stack_empty_have_head(linkstack *L)
{
    if (L == NULL || L->next == NULL)
        return true;
    else
        return false;
}


// 销毁栈,并释放栈S占用的内存空间,适合有头节点的链栈
bool destroy_stack_node_have_head(linkstack **L)
{
    // 头指针的指针本身不存在 或 头指针指向空,没有头节点
    // L 本身是一个二级指针,存放了头指针的地址
    // *L 是头指针,即指向链栈第一个节点的指针
    if (L == NULL || *L == NULL)
        return false;
    linkstack *current;
    while (*L != NULL)
    {
        current = *L;    // 保存当前节点地址
        *L = (*L)->next; // 栈顶指向下一节点
        free(current);   // 释放当前节点
    }
    *L = NULL;
    return true;
}

原文地址:https://blog.csdn.net/qq_43940175/article/details/149816232

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!