C语言 栈与数组

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

C语言 栈与数组

m0_52012656   2022-05-28 我要评论

栈的实现

首先我们思考一个问题,什么是栈?

栈是数据结构的一种,栈在我们日常编码中遇到的非常多,很多人对栈的接触可能仅仅局限在 递归使用的是栈 和 StackOverflowException,栈是一种后进先出的数据结构(可以想象生化金字塔的牢房和生化角斗场的狗洞)。

栈的定义

栈(stack)又名堆栈,它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素

栈的应用广泛,比如你的程序执行查看调用堆栈、计算机四则加减运算、算法的非递归形式、括号匹配问题等等。所以栈也是必须掌握的一门数据结构。最简单大家都经历过,你拿一本书上下叠在一起,就是一个后进先出的过程,你可以把它看成一个栈。下面我们介绍数组实现的栈两种形式。

数组实现

静态栈

一般不推荐使用这种方法,因为当空间不够用时,增容会有点麻烦,不实用。

typedef struct Stack
{ 
    STDataType _a[];  //STDataType 为int宏定义,当然你也可以将它定义为其他类型,宏定义是为了换栈类型的时候方便一点
    int _top; // 栈顶
    int _capacity; // 容量
}Stack;

动态栈

相比静态栈,动态栈空间不够时可以直接时用realloc动态扩容,但是动态扩会有一定程度的消耗,我们会直接扩容一倍,当使用不完时会造成一定程度的空间浪费。

typedef struct Stack
{ 
    STDataType* _a;//指向一块开辟出来的连续空间的指针
    int _top; // 栈顶
    int _capacity; // 容量
}Stack;

栈要实现的操作

// 初始化栈
void StackInit(Stack* ps);
// 入栈
void StackPush(Stack* ps, STDataType data);
// 出栈
void StackPop(Stack* ps);
// 获取栈顶元素
STDataType StackTop(Stack* ps);
// 获取栈中有效元素个数
int StackSize(Stack* ps);
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
bool StackEmpty(Stack* ps);
// 销毁栈
void StackDestroy(Stack* ps);

栈的初始化

void StackInit(Stack* ps)
{
    ps->_a = NULL; //初始化时将指针指向空,此时没有开辟空间
    //这里可以将top赋值为0,也可以赋值为-1。
    ps->_top = -1;  //赋值为0时表示top为栈顶元素的下一个位置的下标,赋值为-1时top为栈顶元素的下标
    ps->_capacity = 0; //栈的容量
}

入栈

void StackPush(Stack* ps, STDataType data)
{
    assert(ps);
    //考虑要不要增容
    //当top为0时判断条件为
    //if(ps->top==ps->_capacity)
    if (ps->_top+1 == ps->_capacity)//当栈满时进入
    {
        //判断当前栈的容量是否为0,为0的话开辟4个空间,不为0时扩容一倍
        int newcapacity = ps->_capacity == 0 ? 4 : ps->_capacity * 2;
        STDataType* tmp = realloc(ps->_a, sizeof(STDataType) * newcapacity);
        if (tmp == NULL)
        {
            exit(-1);
        }
        ps->_a = tmp;
        ps->_capacity = newcapacity;
    }
    //扩容完成,或者不用扩容,开始插入
    ps->_top++;
    ps->_a[ps->_top] = data;
    //当top为0时插入操作
    //ps->_a[ps->_top] = data;
    //ps->_top++;
}

出栈

void StackPop(Stack* ps)
{
    assert(ps);
    //判断是否为空
    assert(!StackEmpty(ps));//暴力判断
    //if (top==-1)//温柔判断
    //{
    //  printf("栈已经空了!\n");
    //  exit(-1); //甩出异常
    //}
    ps->_top--;
}

取栈顶元素

STDataType StackTop(Stack* ps)
{
    assert(ps);
    //判断是否为空,为空甩出异常。
    assert(!StackEmpty(ps));
    //if (!StackEmpty(ps)) {
    //  printf("栈为空!\n");
    //  exit(-1);
    //}
    return ps->_a[ps->_top]; //返回栈顶元素
}

判断栈中有几个有效数据

//取出栈里有效元素个数。
int StackSize(Stack* ps)
{
    assert(ps);
    return ps->_top+1; 
}

判断栈是否为空

bool StackEmpty(Stack* ps)
{
    assert(ps);
    return ps->_top == -1;  //ps->_top为-1返回true,否则返回false.
​
}

销毁栈

销毁栈是必不可少的的一步,如果没有销毁的话会造成内存泄露

void StackDestroy(Stack* ps)
{
    assert(ps);
    free(ps->_a);
    ps->_a = NULL;
    ps->_top = -1;
    ps->_capacity = 0;
}

链栈

最后介绍一下链栈,这里就不实现了有兴趣的话可以自己实现一下。

链栈和链表一样的,也是通指针将各个数据块链接起来的

设计链栈是最好设计为双向链表,否则当你设计为用尾作栈顶是出栈效率低。

用头做栈顶时,头插头删,可以设计为单链表。

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们