【数据结构】使用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;
}
相关推荐
千天夜几秒前
多源多点路径规划:基于启发式动态生成树算法的实现
算法·机器学习·动态规划
从以前6 分钟前
准备考试:解决大学入学考试问题
数据结构·python·算法
.Vcoistnt29 分钟前
Codeforces Round 994 (Div. 2)(A-D)
数据结构·c++·算法·贪心算法·动态规划
小猿_001 小时前
C语言实现顺序表详解
c语言·开发语言
ALISHENGYA1 小时前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(实战训练三)
数据结构·c++·算法·图论
Dola_Pan2 小时前
C语言:随机读写文件、实现文件复制功能
c语言·开发语言
佳心饼干-2 小时前
C语言-08复合类型-结构体
c语言·开发语言
F-2H2 小时前
C语言:指针3(函数指针与指针函数)
linux·c语言·开发语言·c++
我码玄黄3 小时前
正则表达式优化之算法和效率优化
前端·javascript·算法·正则表达式
Solitudefire4 小时前
蓝桥杯刷题——day9
算法·蓝桥杯