[C语言数据结构]栈

简介: [C语言数据结构]栈

1.栈的定义:

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/入栈,插入数据是在栈顶操作;

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

1.2栈的特性:

先进后出

栈的最经典的特性就是先进后出,也可以被叫做后进先出;
这怎么理解呢,就相当于是枪的弹夹,在填装子弹的时候和取出子弹的时候,我们会发现最先放进去的子弹反而是被最后取出的,而最后放入的子弹是最先放进去的;

1.3栈的实现:

分析:对于栈的实现来说,我们有两种数据结构可待选择;

一种是链表,一种是数组;

对于栈的话我们要进行的最多的就是出栈和入栈的操作了。

首先我们来讲数组:数组对于实现入栈出战来说还是很方便的,特别是选用尾部为栈顶的话。只需要的就是数组的扩容问题,只需要调用realloc函数即可。

再其次是链表:对于链表的话我们如果选用单链表的话,若是选用尾部为栈顶的话,我们就很麻烦,每次都需要遍历找到链表的尾指针;

而且相对来说数组的尾插和尾删的效率都很高。

所以这里我们选择先使用数组来实现栈;

1.4代码:

1.4.1结构的声明:

首先我们来分析对于一个用数组来实现的栈来说,我们只需要有一个数组指针,标记栈顶位置的变量,和一个记录栈的容量的变量;

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

1.4.2栈的初始化:

void StackInit(ST* ps);这个函数我们实现的是对于栈的初始化,这个函数的参数是一个结构体类型的指针,我们只需要将结构体内部包含的一个数组指针置空,和两个分别表示栈顶位置的变量和栈的容量的变量都置成零即可。

这里有一个要注意的点就是我们把top也置为0,就是表示top每次指向的是栈顶的后一个位置;(这一点很重要后面的出栈和入栈都会用到)

代码:

//栈的初始化
void StackInit(ST* ps)
{
  assert(ps);
  ps->a = NULL;
  ps->top = 0;
  ps->capacity = 0;
}

1.4.3入栈:

void StackPush(ST* ps, STDataType x);

这个函数来试下入栈的操作,对于入栈来说我们最需要注意的就是空间问题,因为数组的大小是给固定的所以我们要在每次入栈的时候要进行一次判断,判断是否需要扩容,并且这里要注意的是top指的是栈顶的后一个位置,所以我们执行的是先插入数据后,再将top++的;

代码:

//入栈
void StackPush(ST* ps, STDataType x)
{
  assert(ps);
  if (ps->top == ps->capacity)
  {
    int newCapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);
    STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
    if (tmp == NULL)
    {
      printf("realloc fail\n");
      exit(-1);
    }
    ps->a = tmp;
    ps->capacity = newCapacity;
  }
  ps->a[ps->top] = x;
  ps->top++;
}

1.4.4出栈

void StackPop(ST* ps);

对于数组实现的栈来说,出栈的操作非常简单,我们只需要将记录栈顶的位置的变量top--即可。

代码:

//出栈
void StackPop(ST* ps)
{
  assert(ps);
  assert(!StackEmpty(ps));
  ps->top--;
}

1.4.5拿到栈顶的数据
STDataType StackTop(ST* ps);

这个函数实现的就是返回栈顶的数据,只需要将数组中对应栈顶的数据返回即可,这里需要注意的就是,在返回栈顶数据的时候数组的下标用的是top-1;

代码:

//拿到栈顶的数据
STDataType StackTop(ST* ps)
{
  assert(ps);
  assert(!StackEmpty(ps));
  return ps->a[ps->top - 1];
}

1.4.6栈的大小

int StackSize(ST* ps);

这个函数返回的是栈的大小,只需要将top返回即可;
代码:

//栈的大小
int StackSize(ST* ps)
{
  assert(ps);
  return ps->top;
}

1.4.7判断栈是否为空
bool StackEmpty(ST* ps);

如果栈的空间是空,我们就返回假,如果栈的空间是不为空,就返回真;

代码:

//栈是否为空
bool StackEmpty(ST* ps)
{
  assert(ps);
  return ps->top == 0;
}

1.4.8栈的销毁

void StackDestroy(ST* ps);

将数组指针置空,然后其他变量置为0即可;

代码:

//栈的销毁
void StackDestroy(ST* ps)
{
  assert(ps);
  free(ps->a);
  ps->a = NULL;
  ps->capacity = 0;
  ps->top = 0;
}

以上就是栈用数组的实现了!

这里再推荐一个用到栈的力扣题大家可以去试一试练练手:

20. 有效的括号 - 力扣(LeetCode)

2.完整代码

源文件:

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
//栈的初始化
void StackInit(ST* ps)
{
  assert(ps);
  ps->a = NULL;
  ps->top = 0;
  ps->capacity = 0;
}
//入栈
void StackPush(ST* ps, STDataType x)
{
  assert(ps);
  if (ps->top == ps->capacity)
  {
    int newCapacity = (ps->capacity == 0 ? 4 : ps->capacity * 2);
    STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newCapacity);
    if (tmp == NULL)
    {
      printf("realloc fail\n");
      exit(-1);
    }
    ps->a = tmp;
    ps->capacity = newCapacity;
  }
  ps->a[ps->top] = x;
  ps->top++;
}
//栈的销毁
void StackDestroy(ST* ps)
{
  assert(ps);
  free(ps->a);
  ps->a = NULL;
  ps->capacity = 0;
  ps->top = 0;
}
//出栈
void StackPop(ST* ps)
{
  assert(ps);
  assert(!StackEmpty(ps));
  ps->top--;
}
//拿到栈顶的数据
STDataType StackTop(ST* ps)
{
  assert(ps);
  assert(!StackEmpty(ps));
  return ps->a[ps->top - 1];
}
//栈的大小
int StackSize(ST* ps)
{
  assert(ps);
  return ps->top;
}
//栈是否为空
bool StackEmpty(ST* ps)
{
  assert(ps);
  return ps->top == 0;
}

头文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
  STDataType* a;
  int top;
  int capacity;
}ST;
//栈的初始化
void StackInit(ST* ps);
//栈的销毁
void StackDestroy(ST* ps);
//入栈
void StackPush(ST* ps, STDataType x);
//出栈
void StackPop(ST* ps);
//拿到栈顶的数据
STDataType StackTop(ST* ps);
//栈的大小
int StackSize(ST* ps);
//栈是否为空
bool StackEmpty(ST* ps);

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
void Teststack()
{
  ST st;
  StackInit(&st);
  StackPush(&st, 1);
  StackPush(&st, 2);
  StackPush(&st, 3);
  StackPush(&st, 4);
  while (!StackEmpty(&st))
  {
    printf("%d ", StackTop(&st));
    StackPop(&st);
  }
  StackDestroy(&st);
}
int main()
{
  Teststack();
  return 0;
}

以上就是本篇博客的所有内容了,如果帮到你请点赞关注收藏一下!

相关文章
|
22天前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
109 9
|
13天前
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
21 1
|
16天前
|
存储 算法 Java
数据结构的栈
栈作为一种简单而高效的数据结构,在计算机科学和软件开发中有着广泛的应用。通过合理地使用栈,可以有效地解决许多与数据存储和操作相关的问题。
|
21天前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
60 16
|
21天前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
72 8
|
19天前
|
存储 JavaScript 前端开发
执行上下文和执行栈
执行上下文是JavaScript运行代码时的环境,每个执行上下文都有自己的变量对象、作用域链和this值。执行栈用于管理函数调用,每当调用一个函数,就会在栈中添加一个新的执行上下文。
|
21天前
|
存储
系统调用处理程序在内核栈中保存了哪些上下文信息?
【10月更文挑战第29天】系统调用处理程序在内核栈中保存的这些上下文信息对于保证系统调用的正确执行和用户程序的正常恢复至关重要。通过准确地保存和恢复这些信息,操作系统能够实现用户模式和内核模式之间的无缝切换,为用户程序提供稳定、可靠的系统服务。
47 4
|
24天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
50 4
|
24天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
40 0
|
1月前
|
算法 程序员 索引
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器
栈的基本概念、应用场景以及如何使用数组和单链表模拟栈,并展示了如何利用栈和中缀表达式实现一个综合计算器。
33 1
数据结构与算法学习七:栈、数组模拟栈、单链表模拟栈、栈应用实例 实现 综合计算器