【数据结构】使用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;
}
相关推荐
好奇龙猫2 小时前
【学习AI-相关路程-mnist手写数字分类-win-硬件:windows-自我学习AI-实验步骤-全连接神经网络(BPnetwork)-操作流程(3) 】
人工智能·算法
sp_fyf_20242 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-11-01
人工智能·深度学习·神经网络·算法·机器学习·语言模型·数据挖掘
ChoSeitaku3 小时前
链表交集相关算法题|AB链表公共元素生成链表C|AB链表交集存放于A|连续子序列|相交链表求交点位置(C)
数据结构·考研·链表
偷心编程3 小时前
双向链表专题
数据结构
香菜大丸3 小时前
链表的归并排序
数据结构·算法·链表
jrrz08283 小时前
LeetCode 热题100(七)【链表】(1)
数据结构·c++·算法·leetcode·链表
oliveira-time3 小时前
golang学习2
算法
@小博的博客3 小时前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
南宫生4 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步5 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝