数据结构-如何巧妙实现一个栈?逐步解析与代码示例

文章目录

引言

栈是一种基本但强大的数据结构,它在许多算法和系统功能中扮演着关键角色。在这篇文章中,我们将深入探讨如何在实现一个栈,从基本概念到具体的代码实现,再到实际应用场景的探讨。

1.栈的基本概念

在深入代码之前,先简单介绍栈的概念。栈是一个项的有序集合,其中添加(推入)和删除(弹出)项总发生在同一端,称为"栈顶"。他是后进先出的,就好像弹夹里面的子弹一样


2.选择数组还是链表?

数组的优点在于实现简单,访问时间快。但其缺点是大小固定,可能会有空间浪费或不足的问题。

链表的优点在于可以动态调整大小,内存利用率高。但其缺点是相对于数组,访问时间可能会稍慢。

我们用数组来实现


3. 定义栈结构

c 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>

typedef int STDataType;
typedef struct Stack
{
  STDataType* a;
  STDataType top; 
  STDataType capacity;
}ST;

接口

c 复制代码
void STInit(ST* pst);
void STDestroy(ST* pst);
void STPush(ST* pst,STDataType x);
void STPop(ST* pst);
STDataType STTop(ST* pst);
bool STEmpty(ST* pst);
int STSize(ST* pst);

4.初始化栈

初始化是栈实现中的第一步。我们需要将栈顶 top 的初始值设为0,以表示栈为空,在这个实现中,栈被定义为包含动态数组、栈顶指针和容量的结构体。初始化函数 STInit 负责设置这些属性的初始状态。

c 复制代码
// 初始化栈
void STInit(ST* pst)
{
  assert(pst);
  pst->a = NULL;       // 初始时,数组指针为空
  pst->top = 0;        // 栈顶指针初始为0,表示栈为空
  pst->capacity = 0;   // 初始容量为0
}

5.压栈操作

在实现压栈操作时,我们要考虑栈可能满的情况。如果栈已满,我们应该阻止进一步的压栈并给出适当的反馈。

c 复制代码
// 检查并扩展栈的容量
void SLCheckCapacity(ST* pst)
{
  assert(pst);
  if (pst->top == pst->capacity)
  {
    int newCapacity = (pst->capacity == 0) ? 4 : pst->capacity * 2;
    STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newCapacity);
    if (tmp == NULL)
    {
      perror("realloc fail");
      exit(1); // 如果内存分配失败,则退出程序
    }

    pst->a = tmp;
    pst->capacity = newCapacity;
  }
}

// 向栈中推入一个元素
void STPush(ST* pst, STDataType x)
{
  assert(pst);
  SLCheckCapacity(pst); // 检查并扩展容量
  pst->a[pst->top] = x; // 存放元素
  pst->top++;           // 栈顶指针增加
}

6.弹栈操作

弹栈时,我们需要确保栈不为空。如果尝试从空栈中弹出元素,应该返回一个错误指示。

c 复制代码
// 从栈中弹出一个元素
void STPop(ST* pst)
{
  assert(pst);
  assert(pst->top > 0); // 确保栈不为空
  pst->top--;           // 栈顶指针减少
}

7.查看栈顶和判断栈空

这些操作相对简单,但它们对于栈的有效使用至关重要。

c 复制代码
// 获取栈顶元素
STDataType STTop(ST* pst)
{
  assert(pst);
  assert(pst->top > 0); // 确保栈不为空
  return pst->a[pst->top - 1]; // 返回栈顶元素
}

// 检查栈是否为空
bool STEmpty(ST* pst)
{
  assert(pst);
  return pst->top == 0; // 如果栈顶指针为0,则栈为空
}

9.销毁栈操作

在这个静态数组实现中,destroy 函数不是必须的,但是我们使用了动态分配的内存,则需要在此释放内存。

c 复制代码
// 销毁栈
void STDestroy(ST* pst)
{
  assert(pst);
  free(pst->a);        // 释放栈内部的数组空间
  pst->a = NULL;       // 将数组指针置为空
  pst->top = 0;        // 栈顶指针重置为0
  pst->capacity = 0;   // 容量重置为0
}

10.测试并且打印栈内容

c 复制代码
int main()
{
  ST stack;            // 定义栈变量
  ST* pst = &stack;    // pst 指向 stack
  STInit(pst);         // 初始化栈

  // 向栈中添加元素
  STPush(pst, 1);
  STPush(pst, 2);
  STPush(pst, 3);
  STPush(pst, 4);

  // 打印并弹出栈中的元素
  while (!STEmpty(pst))
  {
    printf("%d ", STTop(pst));
    STPop(pst);
  }
  printf("\n");

  STDestroy(pst); // 销毁栈
  return 0;
}

运行结果如下:


栈的实际应用

栈在计算机科学中有着广泛的应用。从函数调用的内存管理到算法中的临时数据存储,栈的应用无处不在。理解栈如何在这些场景中工作,对于充分利用其潜力至关重要。


结论

栈不仅是一种基本的数据结构,而且是理解更复杂系统和算法的基石。通过在c语言中实现和应用栈,我们不仅能够加深对这一结构的理解,还能够提高我们的编程技巧和问题解决能力。我希望这篇文章能够帮助你更好地理解和实现栈。如果你有任何问题或想分享你的经验,请在评论区留言。

相关推荐
开发小能手1236 小时前
答案daanC语言
数据结构·算法
启观川10 小时前
数据结构与算法-第 3 章 常用算法-贪心算法
数据结构·笔记·python·算法·贪心算法
程序员清风11 小时前
堆与优先队列:从 Top K 到任务调度
java·数据结构·算法
suhan11 小时前
【数据结构】四、串:串string、模式匹配BF\KMP、广义表
数据结构·后端·算法
啵啵啵鱼11 小时前
第二章 线性表
java·数据结构·算法
局部有解12 小时前
Hot100 - 三数之和
数据结构·c++·算法
辉艺棉麻小张12 小时前
用 JSON 建模面料规格:成分、克重、支数与公定回潮率字段设计
数据结构
longlongzihan12 小时前
C++ 常用数据结构速查:vector、stack、queue、list、树、图、哈希表等声明与核心操作
数据结构·c++·list
爱读书的小胖16 小时前
LeetCode 27. 移除元素(C)解题分析
数据结构·算法
爱读书的小胖16 小时前
Codewars: A Brave New Wordle(C)解题分析
c语言·数据结构·算法