【数据结构】使用C语言 从零实现一个栈的数据结构

什么是栈?栈是一种特殊的线性表,它只能在在表尾进行插入和删除操作。

栈的底部称为栈底,顶部称为栈顶,所有的操作只能在栈顶进行,也就是说,被压在下方的元素,只能等待其上方的元素出栈之后才能取出,就像我们往箱子里里面放的书一样,因为只有一个口取出里面的物品,所以被压在下面的书只能等上面的书被拿出来之后才能取出,这就是栈的思想,它是一种先进后出的数据结构。

使用C语言实现栈

一般使用栈这样的机构,常用的两个操作就是入栈和出栈

  • pop:出栈操作,将栈顶的元素取出,并删除
  • push:入栈操作,将新元素置入栈顶

定义数据结构

我们在栈的底层使用数组进行存储,所以结构中将储存一个数组的指针,和数组的容量(方便初始化和申请内存);另外栈只能操作栈顶的元素,所以结构中还存着一个变量top,表示栈顶的元素。

c 复制代码
typedef int E;

struct Stack
{
    E * array; // 一个数组的指针
    int capacity; // 数组的容量
    int top;  // 栈顶的元素
};

typedef struct Stack * stack;

再给栈结构的指针起一个别名,便于后续对栈进行操作。

定义初始化方法

接着我们定义一个初始化栈的方法,在初始化方法中,我们默认初始化时,底层数组的最大容量是10个内存单位,也就是40字节(因为E是int类型),接着我们定义,刚初始化完栈之后是一个空栈,则栈顶的指针默认为-1。

top变量是为后续的入栈出栈操作做铺垫的,它可以当作数组的索引,也可以用来当作是否是空栈的标识。

c 复制代码
int initStack(stack stack)
{
    stack->array = malloc(sizeof(E) * 10); // 申请一个40字节的内存
    if (stack->array == NULL) return 0;
    stack->capacity = 10; // 记录底层数组的容量
    stack->top = -1;  // 栈没有元素默认为-1

    return 1;
}

在main函数中,实例化一个Stack类型,接着使用initStack函数就完成了栈的初始化:

c 复制代码
int main()
{
    struct Stack stack;
    initStack(&stack);


    return 0;
}

实现入栈操作

入栈操作,就是把一个元素放到栈的最上方,也就是栈顶上,所以实现该操作的函数只需要有两个参数就可以了:

  • 栈的指针
  • 需要入栈的元素

另外,底层数组的默认容量是10,如果入栈超过10个元素的话,内存就会造成泄露,所以对底层数组进行扩容操作也是必不可少的。

c 复制代码
int pushStack(stack stack,E element)
{
    if (stack->top == stack->capacity - 1) // 扩容
    {
        int newCapacity = stack->capacity * 2;
        E* newArray = realloc(stack->array,newCapacity * sizeof(E));
        if (newArray == NULL) return 0;
        stack->array = newArray;
        stack->capacity = newCapacity;
    }

    stack->top++;
    stack->array[stack->top] = element;
    
    return 1;
}

在扩容操作中,我们使用realloc函数将原数组拷贝到一个新的大小的内存中,这个内存地址我们使用newArray来接收,最后将原栈中的array指向newArray,将原栈中的capacity 指向newCapacity

简单编写一个打印栈元素的函数,用于测试栈:

c 复制代码
void printStack(stack stack)
{
    printf("|");
    for (int i = 0;i<=stack->top;i++)
    {
        printf("%d, ",stack->array[i]);
    }

    printf("\n");
}

int main()
{
    struct Stack stack;
    initStack(&stack);

    for(int i = 1;i<=10;i++)
    {
        pushStack(&stack,i);
    }

    printStack(&stack);

    return 0;
}

控制台输出:

复制代码
|1, 2, 3, 4, 5, 6, 7, 8, 9, 10,

入栈操作成功实现~

实现出栈操作

出栈操作就是把栈顶的元素取出,然后把top执行自减操作,如果不自减元素出栈之后栈顶的元素还会是原来的元素。

c 复制代码
E popStack(stack stack)
{
    if (stack->top == -1)
    {
        printf("栈为空,不能出栈\n");
        return -1;
    }
    
    E element = stack->array[stack->top];
    stack->top--;

    return element;
}

我们接着测试一下:

c 复制代码
void printStack(stack stack)
{
    printf("|");
    for (int i = 0;i<=stack->top;i++)
    {
        printf("%d, ",stack->array[i]);
    }

    printf("\n");
}

int main()
{
    struct Stack stack;
    initStack(&stack);

    for(int i = 1;i<=20;i++)
    {
        pushStack(&stack,i);
    }

    printStack(&stack);

    printf("出栈的元素顺序:");
    while (1)
    {
        printf("%d, ",stack.array[stack.top]);
        popStack(&stack);
        if (stack.top == -1) break;
    }

    return 0;
}

会发现控制台输出的信息为:

复制代码
|1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
出栈的元素顺序:20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,

出栈的顺序是从20到1的,说明元素是从栈顶依次出去的,至此出栈操作也实现好了。

总结

使用C语言手动把栈的结构和各种操作实现一遍是很重要的,它能够加深学习者对计算机底层的认识,也能够为后续的算法题的解题提供一种新的思路。

本篇博文所有代码:

c 复制代码
#include "stdio.h"
#include "stdlib.h"

typedef int E;

struct Stack
{
    E * array; // 一个数组的指针
    int capacity; // 数组的容量
    int top;  // 栈顶的元素
};

typedef struct Stack * stack;

int initStack(stack stack)
{
    stack->array = malloc(sizeof(E) * 10); // 申请一个40字节的内存
    if (stack->array == NULL) return 0;
    stack->capacity = 10; // 记录底层数组的容量
    stack->top = -1;  // 栈没有元素默认为-1

    return 1;
}

int pushStack(stack stack,E element)
{
    if (stack->top == stack->capacity - 1) // 扩容
    {
        int newCapacity = stack->capacity * 2;
        E* newArray = realloc(stack->array,newCapacity * sizeof(E));
        if (newArray == NULL) return 0;
        stack->array = newArray;
        stack->capacity = newCapacity;
    }

    stack->top++;
    stack->array[stack->top] = element;

    return 1;
}

E popStack(stack stack)
{
    if (stack->top == -1)
    {
        printf("栈为空,不能出栈\n");
        return -1;
    }

    E element = stack->array[stack->top];
    stack->top--;

    return element;
}

void printStack(stack stack)
{
    printf("|");
    for (int i = 0;i<=stack->top;i++)
    {
        printf("%d, ",stack->array[i]);
    }

    printf("\n");
}

int main()
{
    struct Stack stack;
    initStack(&stack);

    for(int i = 1;i<=20;i++)
    {
        pushStack(&stack,i);
    }

    printStack(&stack);

    printf("出栈的元素顺序:");
    while (1)
    {
        printf("%d, ",stack.array[stack.top]);
        popStack(&stack);
        if (stack.top == -1) break;

    }

    return 0;
}
相关推荐
月落归舟几秒前
排序算法---(三)
数据结构·算法·排序算法
njidf5 分钟前
C++中的观察者模式
开发语言·c++·算法
2301_822782828 分钟前
C语言数组通关攻略!从一维到字符数组,零基础也能轻松掌握
c语言·算法·数组·编程基础·避坑技巧
zhugby14 分钟前
标号法原理
算法
2301_8227828227 分钟前
C3 vs Zig:2026年,谁才是真正能“修复”C语言的救星?
c语言·zig·c3·系统级开发·语言革新
努力学习的小廉1 小时前
我爱学算法之——动态规划(一)
算法·动态规划
篮l球场1 小时前
前 K 个高频元素
数据结构·算法·leetcode
汉克老师1 小时前
GESP5级C++考试语法知识(十一、递归算法(一))
c++·算法·记忆化搜索·递归算法·递归优化
星夜夏空991 小时前
C语言进阶项目——搭建内存池
c语言·开发语言
历程里程碑1 小时前
Proto3 三大高级类型:Any、Oneof、Map 灵活解决复杂业务场景
java·大数据·开发语言·数据结构·elasticsearch·链表·搜索引擎