链栈(链式栈) 超详细实现(C 语言 + 逐行精讲)

前言

栈(Stack) 是一种后进先出(LIFO)的线性数据结构。** ****前面我们学习了顺序栈** (数组实现),今天我们学习它的兄弟 ------链栈(链式栈)

链栈 = 用单链表实现的栈 它完美解决了顺序栈容量固定、需要扩容的缺点。


一、链栈是什么?

链栈 = 采用链式存储结构实现的栈 本质就是一个只能在表头进行插入、删除操作的单链表

特点:

  • 不需要预先分配空间,想用多少申请多少

  • 不存在栈满溢出问题(除非内存耗尽)

  • 不用扩容

  • 入栈、出栈都在表头完成,效率 O (1)

  • 每个节点包含:数据域 + 指针域


二、链栈结构体设计(核心)

链栈采用 "头结点辅助管理" 模式,结构非常清晰:

cpp 复制代码
#pragma once
typedef char ELEM_TYPE;

// 链栈节点结构体
typedef struct Stack_Node
{
    ELEM_TYPE data;        // 数据域
    struct Stack_Node* next;// 指针域
}Stack_Node, *PStack_Node;

// 链栈辅助结构体(管理栈顶 + 长度)
typedef struct
{
    PStack_Node top;   // 栈顶指针
    int count;         // 节点个数(有效长度)
}LinkStack;

结构说明

  • Stack_Node:真正存储数据的节点

  • LinkStack:辅助管理结构

    • top:永远指向栈顶节点

    • count:记录栈中元素个数,获取长度 O (1)


三、链栈支持的操作(全覆盖)

cpp 复制代码
// 1. 初始化
void Init_Stack(LinkStack* ls);
// 2. 入栈(头插)
bool Push(LinkStack* ls, ELEM_TYPE val);
// 3. 出栈(头删)
bool Pop(LinkStack* ls);
// 4. 获取栈顶元素
ELEM_TYPE Top(LinkStack* ls);
// 5. 判空
bool Is_Empty(LinkStack* ls);
// 6. 获取有效长度
int Get_Length(LinkStack* ls);
// 7. 清空
void Clear(LinkStack* ls);
// 8. 销毁(释放内存)
void Destroy(LinkStack* ls);
// 9. 打印
void Show(LinkStack* ls);

四、核心函数逐行精讲(最关键)

1. 初始化

cpp 复制代码
void Init_Stack(LinkStack* ls)
{
    assert(ls != NULL);
    ls->count = 0;    // 元素个数为0
    ls->top = NULL;   // 栈顶为空
}

作用:栈一开始是空的,栈顶指针为 NULL,长度为 0。


2. 入栈 Push(核心:头插法)

链栈入栈 = 单链表头插 时间复杂度 O(1)

cpp 复制代码
bool Push(LinkStack* ls, ELEM_TYPE val)
{
    assert(ls != NULL);
    // 申请新节点
    Stack_Node* p = (Stack_Node*)malloc(sizeof(Stack_Node));
    if (p == NULL)
        return false;

    p->data = val;
    p->next = ls->top;  // 新节点指向原来栈顶
    ls->top = p;        // 更新栈顶
    ls->count++;        // 长度+1
    return true;
}

3. 出栈 Pop(核心:头删法)

链栈出栈 = 删除链表第一个节点 时间复杂度 O(1)

cpp 复制代码
bool Pop(LinkStack* ls)
{
    assert(ls != NULL);
    if (Is_Empty(ls))
        return false;

    Stack_Node* p = ls->top;
    ls->top = p->next;  // 栈顶后移
    free(p);            // 释放旧栈顶
    ls->count--;        // 长度-1
    return true;
}

4. 获取栈顶元素 Top

cpp 复制代码
ELEM_TYPE Top(LinkStack* ls)
{
	assert(ls != NULL);
	if (Is_Empty(ls))
		exit(1);
	return ls->top->data;
}

5. 判空

cpp 复制代码
bool Is_Empty(LinkStack* ls)
{
	assert(ls != NULL);
	//return ls->top == NULL;//2选1
	return ls->count == 0;
}

6. 获取长度

cpp 复制代码
int Get_Length(LinkStack* ls)
{
    assert(ls != NULL);
    return ls->count; // O(1) 直接返回,不用遍历!
}

用 count 记录长度,不用遍历链表,效率极高!


7. 销毁栈(重要!防止内存泄漏)

cpp 复制代码
void Destroy(LinkStack* ls)
{
    PStack_Node p = ls->top;
    PStack_Node q = NULL;
    while (p != NULL)
    {
        q = p->next;
        free(p);
        p = q;
    }
    ls->count = 0;
    ls->top = NULL;
}

8. 清空

cpp 复制代码
void Clear(LinkStack* ls)
{
	Destroy(ls);
}

9. 打印栈

cpp 复制代码
void Show(LinkStack* ls)
{
    Stack_Node* p = ls->top;
    for (; p != NULL; p = p->next)
    {
        printf("%c ", p->data);
    }
    printf("\n");
}

五、测试主函数(可直接运行)

cpp 复制代码
int main()
{
    LinkStack head;
    Init_Stack(&head);

    Push(&head, 'A');
    Push(&head, 'B');
    Push(&head, 'C');

    Show(&head);      // 输出:C B A
    Pop(&head);
    Show(&head);      // 输出:B A
    printf("%c \n", Top(&head)); // 输出:B

    Destroy(&head);
    return 0;
}

运行结果


六、链栈 VS 顺序栈

特性 顺序栈 链栈
存储结构 连续数组 链式节点
容量 固定 / 需扩容 无限(不溢出)
入栈出栈 O(1) O(1)
内存分配 静态 / 动态 动态
访问速度 快(缓存友好) 较慢
适用场景 已知最大容量 无法预估容量

七、高频考点(必背)

  1. 链栈为什么用头插法? 因为栈只能在栈顶操作,头插 = 栈顶入栈,时间复杂度 O (1)。

  2. 链栈有没有栈满? 一般认为没有,只有内存不足时才会申请失败。

  3. 链栈的栈顶在链表头还是尾? 链表头部

  4. 链栈如何获取长度?count 变量记录,O(1) 返回,不用遍历。

  5. 链栈和顺序栈最大区别? 顺序栈需要扩容,链栈不需要扩容


相关推荐
CoderCodingNo5 小时前
【GESP】C++三级真题 luogu-B4499, [GESP202603 三级] 二进制回文串
数据结构·c++·算法
cmpxr_6 小时前
【C】局部变量和全局变量及同名情况
c语言·开发语言
网安INF6 小时前
数据结构第三章:栈、队列和数组
数据结构
yuannl107 小时前
数据结构----双端队列实现
数据结构
无限进步_8 小时前
【C++】只出现一次的数字 II:位运算的三种解法深度解析
数据结构·c++·ide·windows·git·算法·leetcode
网域小星球8 小时前
C 语言从 0 入门(十七)|结构体指针 + 动态内存 + 文件综合实战
c语言·开发语言·文件操作·结构体指针·动态内存·综合项目
qq_454245038 小时前
通用引用管理框架
数据结构·架构·c#
lcj25119 小时前
【C语言】数据在内存中的存储
c语言·数据结构
旖-旎9 小时前
哈希表(字母异位次分组)(5)
数据结构·c++·算法·leetcode·哈希算法·散列表