数据结构07--链栈超详解

链栈(操作堆空间)

1. 基本概念

栈是一种逻辑结构,是特殊的线性表。特殊在:

  • 只能在固定的一端操作

只要满足上述条件,那么这种特殊的线性表就会呈现一种“后进先出”的逻辑,这种逻辑就被称为栈。栈在生活中到处可见,比如堆叠的盘子、电梯中的人们、嵌套函数的参数等等。

由于约定了只能在线性表固定的一端进行操作,于是给栈这种特殊的线性表的“插入”、“删除”,另起了下面这些特定的名称:

  • 栈顶:可以进行插入删除的一端
  • 栈底:栈顶的对端
  • 入栈:将节点插入栈顶之上,也称为压栈,函数名通常为push()
  • 出栈:将节点从栈顶剔除,也称为弹栈,函数名通常为pop()
  • 取栈顶:取得栈顶元素,但不出栈,函数名通常为top()

基于这种固定一端操作的简单约定,栈获得了“后进先出”的基本特性,如下图所示,最后一个放入的元素,最先被拿出来:

2. 存储形式

栈只是一种线性数据逻辑,如何将数据存储于内存则是另一回事。一般而言,可以采用顺序存储形成顺序栈,或采用链式存储形成链式栈。

  • 顺序栈
  • 链式栈

链式栈的组织形式与链表无异,只不过插入删除被约束在固定的一端。为了便于操作,通常也会创建所谓管理结构体,用来存储栈顶指针、栈元素个数等信息:

3.链栈操作

3.1链栈初始化

// 链栈节点设计
typedef struct node
{
    int data;
    struct node *next;
} Node_t, *P_Node_t;

// 管理链栈结构体设计
typedef struct linkStack
{
    struct node *top;
    int size;
} linkStack;

3.2链队初始化

// @brief 管理链栈结构体初始化
/// @return 返回初始化完成的管理链栈结构体指针
linkStack *stackInit()
{
    // 管理链栈结构体堆内存空间申请
    linkStack *s = (linkStack *)calloc(1, sizeof(linkStack));
    // 判断管理链栈结构体堆内存空间申请是否成功
    if (s == NULL)
    {
        printf("管理链栈结构体堆内存空间申请失败.\n");
        return NULL;
    }
    // 栈顶指针指向NULL
    s->top = NULL;
    // 链栈节点个数初始化为0
    s->size = 0;
    // 返回初始化完成的管理链栈结构体指针
    return s;
}

/// @brief 链栈节点初始化
/// @param newData 新节点数据
/// @return 返回初始化完成的新节点
P_Node_t nodeInit(int newData)
{
    // 新节点堆内存空间申请
    P_Node_t newNode = (P_Node_t)calloc(1, sizeof(Node_t));
    // 判断新节点堆内存空间是否申请成功
    if (newNode == NULL)
    {
        printf("新节点堆内存空间申请失败.\n");
        return NULL;
    }
    // 新节点数据初始化
    newNode->data = newData;
    // 新节点指向NULL
    newNode->next = NULL;
    // 返回初始化完成的新节点
    return newNode;
}

3.3入栈(压栈)

/// @brief 压栈,即新节点入栈
/// @param s 管理链栈结构体
/// @param newNode 新节点
/// @return 压栈成功返回真,失败返回假
bool push(linkStack *s, P_Node_t newNode)
{
    // 判断管理链栈结构体或者新节点是否为空
    if (s == NULL || newNode == NULL)
    {
        printf("管理链栈结构体或者新节点为空.\n");
        return false;
    }
    // 新节点指向栈顶
    newNode->next = s->top;
    // 栈顶指向新节点
    s->top = newNode;
    // 栈元素个数+1
    s->size++;
    // 入栈成功返回真
    return true;
}

3.4出栈

/// @brief 出栈
/// @param s 管理链栈结构体指针
/// @param outData 出栈节点数据
/// @return 出栈成功返回真,失败返回假
bool pop(linkStack *s, int *outData)
{
    // 判断链栈是否为空
    if (s->size == 0)
    {
        printf("链栈为空.\n");
        return false;
    }
    // tmp指向出栈节点
    P_Node_t tmp = s->top;
    //*outData取得出栈节点数据
    *outData = tmp->data;
    // 栈顶指向下一个元素
    s->top = s->top->next;
    // 栈元素个数-1
    s->size--;
    // 出栈成功返回真
    return true;
}

3.5遍历链栈

/// @brief 遍历栈元素
/// @param s 管理栈结构体
void display(linkStack *s)
{
    // 判断栈是否为空
    if (s->size == 0)
    {
        printf("链栈为空.\n");
        return;
    }
    printf("当前链栈:\n");
    // tmp遍历栈
    for (P_Node_t tmp = s->top; tmp != NULL; tmp = tmp->next)
    {
        printf("%d\n", tmp->data);
    }
    return;
}

3.6 销毁链栈

    // 释放链队节点堆内存空间
    while (q->size != 0)
    {
        outQueue(q, &outData);
    }
    // 释放管理链队结构体
    free(q);

3.7代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
// 链栈节点设计
typedef struct node
{
    int data;
    struct node *next;
} Node_t, *P_Node_t;
// 管理链栈结构体设计
typedef struct linkStack
{
    struct node *top;
    int size;
} linkStack;
/// @brief 管理链栈结构体初始化
/// @return 返回初始化完成的管理链栈结构体指针
linkStack *stackInit()
{
    // 管理链栈结构体堆内存空间申请
    linkStack *s = (linkStack *)calloc(1, sizeof(linkStack));
    // 判断管理链栈结构体堆内存空间申请是否成功
    if (s == NULL)
    {
        printf("管理链栈结构体堆内存空间申请失败.\n");
        return NULL;
    }
    // 栈顶指针指向NULL
    s->top = NULL;
    // 链栈节点个数初始化为0
    s->size = 0;
    // 返回初始化完成的管理链栈结构体指针
    return s;
}
/// @brief 链栈节点初始化
/// @param newData 新节点数据
/// @return 返回初始化完成的新节点
P_Node_t nodeInit(int newData)
{
    // 新节点堆内存空间申请
    P_Node_t newNode = (P_Node_t)calloc(1, sizeof(Node_t));
    // 判断新节点堆内存空间是否申请成功
    if (newNode == NULL)
    {
        printf("新节点堆内存空间申请失败.\n");
        return NULL;
    }
    // 新节点数据初始化
    newNode->data = newData;
    // 新节点指向NULL
    newNode->next = NULL;
    // 返回初始化完成的新节点
    return newNode;
}
/// @brief 压栈,即新节点入栈
/// @param s 管理链栈结构体
/// @param newNode 新节点
/// @return 压栈成功返回真,失败返回假
bool push(linkStack *s, P_Node_t newNode)
{
    // 判断管理链栈结构体或者新节点是否为空
    if (s == NULL || newNode == NULL)
    {
        printf("管理链栈结构体或者新节点为空.\n");
        return false;
    }
    // 新节点指向栈顶
    newNode->next = s->top;
    // 栈顶指向新节点
    s->top = newNode;
    // 栈元素个数+1
    s->size++;
    // 入栈成功返回真
    return true;
}
/// @brief 出栈
/// @param s 管理链栈结构体指针
/// @param outData 出栈节点数据
/// @return 出栈成功返回真,失败返回假
bool pop(linkStack *s, int *outData)
{
    // 判断链栈是否为空
    if (s->size == 0)
    {
        printf("链栈为空.\n");
        return false;
    }
    // tmp指向出栈节点
    P_Node_t tmp = s->top;
    //*outData取得出栈节点数据
    *outData = tmp->data;
    // 栈顶指向下一个元素
    s->top = s->top->next;
    // 栈元素个数-1
    s->size--;
    // 出栈成功返回真
    return true;
}
/// @brief 遍历栈元素
/// @param s 管理栈结构体
void display(linkStack *s)
{
    // 判断栈是否为空
    if (s->size == 0)
    {
        printf("链栈为空.\n");
        return;
    }
    printf("当前链栈:\n");
    // tmp遍历栈
    for (P_Node_t tmp = s->top; tmp != NULL; tmp = tmp->next)
    {
        printf("%d\n", tmp->data);
    }
    return;
}
int main(int argc, char const *argv[])
{
    // 初始化管理链栈结构体
    linkStack *s = stackInit();
    char optMenu;
    P_Node_t newNode = NULL;
    int data, outData = 0, overFlag = 0;
    while (1)
    {
        printf("输入i入栈\t输入o出栈\t输入d打印当前链栈\tt.退出\n");
        scanf("%c", &optMenu);
        switch (optMenu)
        {
        case 'i':
            printf("请输入数据入栈:\n");
            scanf("%d", &data);
            newNode = nodeInit(data);
            if (push(s, newNode))
            {
                printf("入栈成功.\n");
            }
            else
            {
                printf("入栈失败.\n");
            }
            break;
        case 'o':
            if (pop(s, &outData))
            {
                printf("出栈成功,出栈的数据为:%d\n", outData);
            }
            else
            {
                printf("出栈失败.\n");
            }
            break;
        case 'd':
            display(s);
            break;
        case 't':
            overFlag = 1;
            break;
        default:
            printf("输入错误,请重新输入.\n");
            break;
        }
        if (overFlag)
        {
            break;
        }
        while (getchar() != '\n')
            ;
    }
    // 释放栈元素
    while (s->size != 0)
    {
        // 出栈
        pop(s, &outData);
    }
    // 释放链栈
    free(s);
    return 0;
}

4链栈示例:十进制转八进制、十六进制

/**
 * 写一个十进制转8进制,10进制转16进制代码(链栈)
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
typedef struct node
{
    char data;
    struct node *next;
} Node_t, *P_Node_t;
typedef struct linkStack
{
    int size;
    struct node *top;
} linkStack;
linkStack *stackInit()
{
    linkStack *s = (linkStack *)calloc(1, sizeof(linkStack));
    if (s == NULL)
    {
        printf("管理链栈结构体堆内存空间申请失败.\n");
        return NULL;
    }
    s->size = 0;
    s->top = NULL;
    return s;
}
P_Node_t nodeInit(char newData)
{
    P_Node_t newNode = (P_Node_t)calloc(1, sizeof(Node_t));
    if (newNode == NULL)
    {
        printf("新节点堆内存空间申请失败.\n");
        return NULL;
    }
    newNode->data = newData;
    newNode->next = NULL;
    return newNode;
}
bool push(linkStack *s, P_Node_t newNode)
{
    if (s == NULL || newNode == NULL)
    {
        printf("管理链栈结构体或者新节点地址为空.\n");
        return false;
    }
    newNode->next = s->top;
    s->top = newNode;
    s->size++;
    return true;
}
bool pop(linkStack *s)
{
    if (s->size == 0)
    {
        printf("链栈为空.\n");
        return false;
    }
    P_Node_t tmp = s->top;
    s->top = s->top->next;
    s->size--;
    free(tmp);
    return true;
}
void display(linkStack *s)
{
    if (s->size == 0)
    {
        printf("链栈为空.\n");
        return;
    }
    for (P_Node_t tmp = s->top; tmp != NULL; tmp = tmp->next)
    {
        printf("%c", tmp->data);
    }
    return;
}
int main(int argc, char const *argv[])
{
    linkStack *s = stackInit();
    char menuOpt;
    int overFlag = 0;
    long long num;
    printf("请选择进制转换器:\n");
    while (1)
    {
        printf("a.10进制转8进制\tb.10进制转16进制代码.\tt.退出\n");
        scanf("%c", &menuOpt);
        switch (menuOpt)
        {
        case 'a':
            printf("请输入要转换的数字.\n");
            while (scanf("%lld", &num) != 1 || num == -1)
            {
                printf("输入错误,请重新输入.\n");
                while (getchar() != '\n')
                    ;
            }
            while (num != 0)
            {
                push(s, nodeInit("01234567"[num % 8]));
                num /= 8;
            }
            printf("转换后的八进制结果为:\n0");
            display(s);
            printf("\n");
            while (s->size != 0)
            {
                pop(s);
            }
            break;
        case 'b':
            printf("请输入要转换的数字.\n");
            while (scanf("%lld", &num) != 1 || num == -1)
            {
                printf("输入错误,请重新输入.\n");
                while (getchar() != '\n')
                    ;
            }
            while (num != 0)
            {
                push(s, nodeInit("0123456789ABCDEF"[num % 16]));
                num /= 16;
            }
            printf("转换后的十六进制结果为:\n0x");
            display(s);
            printf("\n");
            while (s->size != 0)
            {
                pop(s);
            }
            break;
        case 't':
            overFlag == 1;
            break;
        default:
            printf("输入错误,请重新输入.\n");
            break;
        }
        if (overFlag)
        {
            free(s);
            break;
        }
        while (getchar() != '\n')
            ;
    }
    return 0;
}

5.结语

        在这篇博客中,我们深入探讨了链栈的基本概念、实现方式以及其在实际应用中的优势。链栈作为一种灵活且高效的数据结构,充分利用了指针的特性,使得栈的操作能够在动态内存中进行,避免了固定大小数组的局限性。通过实现基本操作如入栈、出栈和栈顶元素访问,我们不仅巩固了对链表和栈的理解,也为在更复杂的数据结构和算法中的应用奠定了基础。

        掌握链栈的原理和实现将为我们在进行深度优先搜索、解析表达式等任务时提供重要的支持。希望读者在使用链栈的过程中能够灵活应对各种挑战,提升编程能力。

        感谢您的阅读,如有任何问题或建议,欢迎在评论区留言讨论!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值