栈的顺序存储与链式存储

栈的基本概念

栈的定义

(Stack):是只允许在一端进行插入或删除的线性表。首先栈是一种线性表,但限定这种线性表只能在某一端进行插入和删除操作,即先进后出。

栈顶(Top):线性表允许进行插入删除的那一端。
栈底(Bottom):固定的,不允许进行插入和删除的另一端。
空栈:不含任何元素的空表。

栈的常见基本操作

InitStack:初始化一个空栈S。
StackEmpty:判断一个栈是否为空,若栈为空则返回true,否则返回false。
Push:进栈(栈的插入操作),若栈S未满,则加入新元素使之成为新栈顶。
Pop:出栈(栈的删除操作),若栈S非空,则弹出栈顶元素。
GetTop:读栈顶元素,若栈S非空,则返回栈顶元素。
DestroyStack:栈销毁,并释放S占用的存储空间。


栈的顺序存储

设计思想

        采用顺序存储的栈称为顺序栈,它利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶元素的位置。
        若存储栈的长度为N,则栈顶位置top必须小于N。当栈存在一个元素时,top等于0,因此通常把空栈的判断条件定位top等于-1。

栈的顺序存储结构可以描述为:

#define N 1024  //栈的最大的容量
//顺序栈的实现通过数组实现
typedef int sqstack_data_t;

typedef struct sqstack {
    int top; //栈顶位置
    sqstack_data_t data[N];
}ssk_node, * ssk_pnode;

顺序栈的基本操作有

//创建
ssk_pnode create_sqstack();
//出栈
sqstack_data_t pop_sqstack(ssk_pnode S);
//入栈
int push_sqstack(ssk_pnode S, sqstack_data_t data);
//判空
int empty_sqstack(ssk_pnode S);
//判满
int full_sqstack(ssk_pnode S);
//清空
int clean_sqstack(ssk_pnode S);
//销毁
int destroy_sqstack(ssk_pnode *S);

顺序栈的基本算法实现

初始化:将栈开辟在堆区

//创建
ssk_pnode create_sqstack()
{
    ssk_pnode S = (ssk_pnode)malloc(sizeof(ssk_node));
    if (NULL == S)
    {
        printf("malloc is default\n");
        return NULL;
    }
    //printf("!!!\n");
    S->top = -1;  //top代表栈顶位置,栈顶就是top,而不是top-1
    return S;
}

这里需要注意的是,若初始化时top等于-1,则入栈操作后,top指向栈顶元素位置,若初始化时top=0,则入栈操作后,top指向栈顶元素的下一个位置。

判空

//判空
int empty_sqstack(ssk_pnode S)
{
    if (-1 == S->top)
        return 0;
    else
        return -1;
}

 判满

//判满
int full_sqstack(ssk_pnode S)
{
    if (N - 1 == S->top)
        return 0;
    else
        return -1;
}

注意,因为初始化时top我们置为了-1,则top=N-1时栈就已经存满了

入栈

//入栈
int push_sqstack(ssk_pnode S, sqstack_data_t data)
{
    if (0 == full_sqstack(S))
    {
        printf("S is full\n");
        return -1;
    }

    /*
     S->top++;
     S->data[S->top] = data;
     */

    S->data[++S->top] = data;
    return 0;
}

入栈的基本要点有两个:

一是在入栈时需要进行判满,若已经满栈,则不能在进行入栈 。

二是 对于top的操作,因为初始化时top置为-1,故每次入栈之前需要先对top进行自增1。

出栈

//出栈
sqstack_data_t pop_sqstack(ssk_pnode S)
{
    if (0 == empty_sqstack(S))
    {
        printf("S is empty\n");
        return -1;
    }

    return S->data[S->top--];

}

出栈之前注意判空,若是空栈,则没有必要进行出栈操作 

清空

//清空
int clean_sqstack(ssk_pnode S)
{
    if (0 == empty_sqstack(S))
    {
        printf("S is empty\n");
        return -1;
    }

    S->top = -1;

    return 0;
}

顺序栈的清空直接让top恢复初始的-1即可,应为顺序栈是利用数组实现的,对数组的清空就是直接让其指针指向首地址即可。 

销毁

//销毁
int destroy_sqstack(ssk_pnode* S)
{
    if (*S == NULL)
        return -1;
    free(*S);
    *S == NULL;
}

销毁时需要注意的是,函数传入的是地址,销毁的目的是要释放掉主调函数中的堆区,若以值得方式传入,则并不能释放掉驻点函数的堆区空间。换言之,对于需要改变指针变量的指向的函数均需要以地址的方式传入。

栈的链式存储

链栈的定义

        采用链式存储的栈称为链栈,链栈的优点是便于多个栈共享存储空间和提高其效率,且不存在栈满上溢的情况。通常采用单链表实现。通常规定所有操作都是在单链表的表头进行的。

对于链栈的设计有两种模式:

1、表头做栈顶:操作时比较方便,入栈是头插,出栈是头删

2、表尾做栈顶:操作比较麻烦,因为不管是入栈出栈操作均需要去 遍历到链表的尾结点,操作比较繁琐,因此不推荐这种方式

 链栈的基本结构和常用操作如下:

typedef int linkstack_data_t;

typedef struct linkstack {
    linkstack_data_t data;
    struct linkstack* next;
}lsk_node, * lsk_pnode;
//初始化
lsk_pnode create_linkstack();
//入栈
int push_linkstack(lsk_pnode S, linkstack_data_t data);
//出栈
linkstack_data_t pop_linkstack(lsk_pnode S);
//判空
int empty_linkstack(lsk_pnode S);
//清空
int clean_linkstack(lsk_pnode S);
//销毁
int destroy_linkstack(lsk_pnode* S);

 链栈的基本操作实现

初始化

//初始化
lsk_pnode create_linkstack()
{
    lsk_pnode S = (lsk_pnode)malloc(sizeof(lsk_node));
    if (NULL == S)
    {
        printf("malloc is default\n");
        return NULL;
    }
    S->next = NULL;

    return S;
}

        创建一个链栈S,作为头结点,头结点的next指向空。当然,也可以不用定义头结点,直接让S置为空,入栈后让S指向栈顶。

判空

//判空
int empty_linkstack(lsk_pnode S)
{
    if (NULL == S->next)
        return 0;
    else
        return -1;

}

因为我们使用了带头节点的链栈,所以当S->next=NULL即表示为空栈。

判满:链式存储理论上没有存满了的情况

入栈

入栈即是对链表做头插

//入栈
int push_linkstack(lsk_pnode S, linkstack_data_t data)
{
    lsk_pnode new = create_linkstack();
    if (NULL == new)
    {
        return -1;
    }
    new->data = data;

    new->next = S->next;
    S->next = new;

    return 0;
}

出栈

出栈即是对链栈的头删

//出栈
linkstack_data_t pop_linkstack(lsk_pnode S)
{
    if (0 == empty_linkstack(S))
    {
        printf("S is empty\n");
        return -1;
    }

    lsk_pnode dele = S->next;
    S->next = dele->next;
    linkstack_data_t data = dele->data;
    free(dele);
    dele = NULL;
    return data;
}

清空

注意,同顺序栈不同的是,链栈的清空需要释放掉除头结点以外的所有节点

//清空
int clean_linkstack(lsk_pnode S)
{
    if (0 == empty_linkstack(S))
    {
        printf("S is empty\n");
        return -1;
    }
    lsk_pnode p = S->next;
    lsk_pnode q = S->next;
    while (p)
    {
      
        q = q->next;
        free(p);
        p = q;
    }
    S->next = NULL;

    return 0;
}

销毁

同理,销毁链栈时,也需要传入地址进行操作

//销毁
int destroy_linkstack(lsk_pnode* S)
{

    if (0 == empty_linkstack(*S))
    {
        free(*S);
        *S = NULL;
    }
    else
    {
        clean_linkstack(*S);

        free(*S);
        *S = NULL;
    }

    return 0;
}

 总结

        顺序栈与链栈即是分别对顺序存储与链式存储的一个升级,增加了两个限制条件,一是只能从一端插入和删除,二是先进先出。栈的用处比较广泛,例如求斐波那契数列、中缀表达式转后缀表达式等都会使用到栈。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值