• 顺序栈与链式栈


    目录

    栈的定义与结构

    栈的实现

    顺序栈的实现

     初始化空栈

     销毁栈

    压栈

    获取栈顶元素

    出栈

    判断栈是否为空

    获取栈中有效数据的个数

    链式栈的实现

    初始化空栈

     销毁栈

    压栈

    获取栈顶元素

    出栈

    判断栈是否为空

    获取栈中有效数据的个数

    顺序栈与链栈的对比


    栈的定义与结构

    栈是只允许在固定的一端进行插入元素或删除元素的线性表;进行数据插入和删除操作的一端称为栈顶(top), 另一端称为栈底(bottom);栈中的数据元素遵循后进先出(Last In First Out)的原则;

    栈的基本操作:

    压栈:栈中插入元素的操作叫做压栈/入栈,插入数据在栈顶插入;

    出栈:栈中删除元素的操作叫做出栈,删除数据也在栈顶;

    栈的实现

    栈可以由顺序表和链表实现,使用顺序存储结构实现栈存储结构称为顺序栈或数组栈,使用链式结构实现栈存储结构称为链式栈;

    • 顺序栈的优点:

    顺序栈的底层采用的是数组,当在栈顶插入或者删除元素时,可以通过下标进行插入或者删除,方便快捷,而且由于在内存中连续存储,缓存利用率高;

    • 链式栈的缺点:

    链式栈当插入数据(尾插)可以通过定义尾指针实现插入,但是当删除数据(尾删)时,需要找到 尾结点的前一个结点,而单向链表无法实现,只能采用双向链表,代价过大;

    以单链表头结点作为栈顶,此时插入数据(头插),删除数据(头删),这样相比上述方案方便快捷,但是由于是链式存储,缓存利用率低;

    顺序栈的实现

    采用动态顺序表实现栈,当进行插入元素的操作时,若空间不够使用,可以通过动态内存函数realloc()函数扩容;

    1. typedef int STDataType;
    2. typedef struct Stack
    3. {
    4. STDataType* nums;
    5. int top;//记录栈顶元素在顺序栈中的位置;
    6. int capacity;//记录栈的大小;
    7. }Stack;

     初始化空栈

    栈顶元素的位置与栈底元素的位置相同,说明栈为空栈;空栈时,top的取值应该为多少?

    若top=0表示空栈 ,当插入一个元素A0,则top=top+1即top=1;而top记录的是栈顶元素在顺序栈中的位置,此时top指向栈顶元素的下一个位置;若top不自增,top仍然为0,那么也将无法区分栈为空还是存在一个数据;

    结论:top=0时,意味着top是栈顶元素的下一个位置;

    若top=-1表示空栈,当插入1个元素A0,则top=top+1即top=0;由于top记录的是栈顶元素在顺序栈中的位置,逻辑自洽;

    结论:top=-1时,意味着top是栈顶元素位置;

    1. void InitStack(Stack* ps)
    2. {
    3. assert(ps != NULL);
    4. //开辟一定量的空间
    5. ps->nums = (STDataType*)malloc(4*sizeof(STDataType));
    6. //判断空间开辟是否成功
    7. if (ps->nums == NULL)
    8. {
    9. perror("malloc failed:");
    10. exit(-1);
    11. }
    12. ps->top = -1;
    13. ps->capacity = 4;
    14. }

     销毁栈

    1. //栈的销毁
    2. void DestroyStack(Stack* ps)
    3. {
    4. assert(ps != NULL);
    5. free(ps->nums);
    6. ps->nums = NULL;
    7. ps->top = 0;
    8. ps->capacity = 0;
    9. }

    压栈

    压栈时需要先检查栈的容量,若容量已满,需要进行扩容;

    • 栈满情况:(top=capacity-1)

    1. //压栈
    2. void StackPush(Stack* ps, STDataType x)
    3. {
    4. assert(ps != NULL);
    5. //栈满扩容
    6. if (ps->top == ps->capacity - 1)
    7. {
    8. STDataType* tmp = (STDataType*)realloc(ps->nums, sizeof(STDataType)* 2 * (ps->capacity));
    9. if (tmp == NULL)
    10. {
    11. perror("realloc failed:");
    12. exit(-1);
    13. }
    14. ps->nums = tmp;
    15. ps->capacity = 2 * (ps->capacity);
    16. }
    17. //插入数据
    18. ps->nums[ps->top + 1]=x;
    19. ps->top++;
    20. }

    获取栈顶元素

    1. //获取栈顶元素
    2. STDataType StackTop(Stack* ps)
    3. {
    4. assert(ps);
    5. //空栈
    6. assert(ps->top > (-1));
    7. return ps->nums[ps->top];
    8. }

    出栈

    1. void StackPop(Stack* ps)
    2. {
    3. assert(ps);
    4. //空栈时不可进行删除操作
    5. assert(ps->top > -1);
    6. ps->top = ps->top - 1;
    7. }

    判断栈是否为空

    检测栈是否为空,栈为空返回true,否则返回false;

    c语言默认没有布尔值(true/false),使用时需包含头文件# include

    1. bool StackEmpty(Stack* ps)
    2. {
    3. assert(ps);
    4. return (ps->top == -1);
    5. }

    获取栈中有效数据的个数

    1. int StackSize(Stack* ps)
    2. {
    3. assert(ps);
    4. return ps->top + 1;
    5. }
    • 测试如上接口函数:
    1. //遍历栈中元素时,通过一边获取栈顶元素,一边删除的操作实现
    2. int main()
    3. {
    4. Stack st;
    5. InitStack(&st);
    6. StackPush(&st, 10);
    7. StackPush(&st, 20);
    8. StackPush(&st, 30);
    9. StackPush(&st, 40);
    10. StackPush(&st, 50);
    11. while (!StackEmpty(&st))
    12. {
    13. printf("%d->", StackTop(&st));
    14. StackPop(&st);
    15. }
    16. printf("\n");
    17. DestroyStack(&st);
    18. return 0;
    19. }

    运行结果:

    链式栈的实现

    采用带哨兵位的单链表实现,见下图存储结构

    1. //链栈数据结点类型LinkStack声明
    2. typedef int LSDataType;
    3. typedef struct LinkStack
    4. {
    5. LSDataType data;//数据域
    6. struct LinkStack* next;//指针域
    7. }LinkStack;

    链栈的4要素:

    1.栈空条件:phead->next=NULL

    2.栈满条件:对于链栈而言,基本不存在栈满的情况,除非内存已经没有可以使用的空间,不需要考虑;

    3.进栈操作:头插

    4.出栈操作:头删

    初始化空栈

    建立一个空栈实际上是创建哨兵位的结点,并将其的next置为空指针;

    1. //为将哨兵位结点带出,采用返回值的方案
    2. LinkStack* InitStack(LinkStack* phead)
    3. {
    4. LinkStack* phead = (LinkStack*)malloc(sizeof(LinkStack));
    5. if (phead == NULL)
    6. {
    7. perror("malloc fail:");
    8. exit(-1);
    9. }
    10. phead->next = NULL;
    11. return phead;
    12. }

     销毁栈

    释放所有动态开辟的结点空间;

    1. //销毁栈
    2. //先保存当前结点的下一个结点,然后释放当前结点;
    3. void DestroyStack(LinkStack* phead)
    4. {
    5. assert(phead);
    6. LinkStack* cur = phead;
    7. LinkStack* curnext = cur->next;
    8. while (curnext != NULL)
    9. {
    10. free(cur);
    11. cur = curnext;
    12. curnext = cur->next;
    13. }
    14. //此时cur指向尾结点
    15. free(cur);
    16. cur = NULL;
    17. }

    压栈

    1. void StackPush(LinkStack* phead, LSDataType x)
    2. {
    3. assert(phead);
    4. //开辟新结点
    5. LinkStack* newnode = malloc(sizeof(LinkStack));
    6. if (newnode==NULL)
    7. {
    8. perror("malloc failed:");
    9. exit(-1);
    10. }
    11. newnode->data = x;
    12. //头插=压栈
    13. newnode->next = phead->next;
    14. phead->next = newnode;
    15. }

    获取栈顶元素

    在栈不为空的条件下,将哨兵位结点后的头结点的数据返回;

    1. //获取栈顶元素
    2. LSDataType LinkStTop(LinkStack* phead)
    3. {
    4. assert(phead);
    5. //空栈
    6. assert(phead->next != NULL);
    7. return phead->next->data;
    8. }

    出栈

    1. //出栈
    2. void StackPop(LinkStack* phead)
    3. {
    4. assert(phead);
    5. //空栈不可删除
    6. assert(phead->next != NULL);
    7. LinkStack* Second = phead->next->next;
    8. free(phead->next);//释放首结点
    9. phead->next = Second;
    10. }

    判断栈是否为空

    检测栈是否为空,栈为空返回true,否则返回false;

    1. //判断栈是否为空
    2. bool StackEmpty(LinkStack* phead)
    3. {
    4. assert(phead);
    5. return (phead->next == NULL);
    6. }

    获取栈中有效数据的个数

    1. //获取栈中有效数据的个数
    2. int StackSize(LinkStack* phead)
    3. {
    4. assert(phead);
    5. int count = 0;//计数
    6. LinkStack* cur = phead->next;
    7. while (cur != NULL)
    8. {
    9. count++;
    10. cur = cur->next;
    11. }
    12. return count;
    13. }

    顺序栈与链栈的对比

    顺序栈与链栈的时间复杂度均为O(1);

    空间性能由于顺序栈是动态内存函数开辟的,势必会有一定空间的浪费,但是存取定位方便;链栈恰好弥补了上述缺点,但是链栈缓存利用率不高;

    当线性表中元素个数变化大或者不知道元素个数有多少,采用链式栈;

    当事先知道线性表的大致长度,采用顺序栈存储结构效率高;

     

     

     

     

     

     

  • 相关阅读:
    什么样的跨网数据摆渡系统,能够减少数据泄密的风险?
    5 个开源的 Rust Web 开发框架,你选择哪个?
    Servlet全生命周期
    Windows密码凭证获取学习
    【BOOST C++ (5)算法】【04】Containers
    Spring Boot 3.x Web MVC实战:实现流缓存的request
    【YOLO系列】YOLOv2
    C语言 ——宽字符
    生成式人工智能(AIGC):开发者的得力助手还是职业威胁?
    B+树索引(6)之MyISAM索引方案
  • 原文地址:https://blog.csdn.net/m0_58963318/article/details/133676734