顺序栈和链栈的定义和使用C语言实现(附有完整代码)

简介: 顺序栈和链栈的定义和使用C语言实现(附有完整代码)

栈的基本内容:

无论是我们接下来要讲的栈还是后面要讲到的队列,他们虽然在名字上不同于我们之前的顺序表或者单链表,但是它们本质也是线性表,只是在基本操作上没有表那么“自由”。比如:栈只能从栈顶进行插入和删除,而队列只能从对头进行删除,队尾进行插入。


举例:

叠放在一起的盘子,当想要加入新的盘子时,只能在底部或者尾部加入,删除同样也是。

空栈:

栈顶和栈底:

顺序栈

既然上文都说到“栈”和“队列”都是一种“特殊的”线性表”,那么顺序栈,顾名思义就是按照顺序存储的栈。

定义:

既然是顺序存储的,那么我们依然可以和顺序表相类似,采用数组去存放!

typedef struct {
  int data[size];
  int top;//栈顶指针
}seqstack;//seqstack为结构体类型


入栈操作:

对于顺序表的插入操作,我们在栈中叫做“入栈”由于栈的特殊性,只能在栈顶进行操作。


需要提醒的是:一定是栈顶指针先进行移动,再将新插入的元素赋给栈顶空间。也就是说S.top = S.top + 1;S.data[S.top] = x;的顺序不能发生颠倒。

void Pushstack(seqstack& S)
{
  if (S.top == size)
    printf("栈满,拒绝元素继续入栈!");
  else {
    printf("请依次输入你要入栈的元素:\n");
    int x,i;
    for (i = 0; i < size; i++) {
      scanf("%d", &x);
      S.top = S.top + 1;
      S.data[S.top] = x;
      printf("入栈成功!\n");
    }
  }
}

举例:

出栈:

虽然是“出栈”,但是如果后续没有入栈操作对出栈位置进行数据覆盖的话,其实出栈并不是真正意义上的“消失”,只是在逻辑上上被删除了,其实给出下标地址,依然可以找到该元素。**return S.data[S.top];**就是将该元素的值返回,以便下次能够快速找到。

int  PopStack(seqstack& S) {
  if (S.top == -1) {
    printf("栈为空,没有元素输出!");
  }
  {
    printf("当前栈顶元素为:");
     return S.data[S.top];
     S.top = S.top - 1;
  }
}

关于顺序栈的其他操作都是比较简单的,这里就不一一进行讲解了,需要注意的事项我会在下面的完整代码中注释出来!

顺序栈的缺点:

栈的大小不可发生变化。

出栈顺序的计算方法:

如上图所示:

进栈顺序为a->b->c->d->e,则对应的出栈顺序为e->d->c->b->a

但有时候出栈和进栈是穿插进行的:

举例:

这种进栈出栈穿插的方式有很多种。


由此,我们可以得出一个结论:

链栈:

既然上文都说到“栈”和“队列”都是一种“特殊的”线性表”,那么链栈,顾名思义就是按照链式存储的栈。

基本实现方法和单链表相同,这里就不一一进行讲解了,需要注意的事项我会在下面的完整代码中注释出来!

链栈完整代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#define size 5
typedef struct LinkNode {
  int data;
  struct LinkNode* next;
}Linkstack;
//初始化
void Iniststack(Linkstack *L) {
  L= (LinkNode*)malloc(sizeof(LinkNode));
  if (!L->data) {
    printf("申请失败");
  }
  else
  {
    L->data = 0;
    L->next = NULL;
  }
}
//入栈
void Pushstack(Linkstack *L) {
  int e,x;
  printf("请输入你要创建的链栈长度:");
  scanf("%d", &x);
  printf("请输入你要入栈的元素:\n");
  for (int i = 0; i < x; i++) {
    LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
    scanf("%d", &e);
    s->data = e;
    s->next = L->next;
    L->next = s;
  } 
}
//出栈
int  Popstack(Linkstack* L)
{
  LinkNode* s= L->next;
  s->data = L->data;
  L->next = s->next;
  return s->data;
}
//读取栈顶元素
int  Getstack(Linkstack* L) {
  if (!L->data)
  {
    printf("栈为空!");
  }
  else {
    int e = L->next->data;
    return e;
  }
}
//输出栈中元素
void Printsatck(Linkstack* L) {
  if (!L->data)
  {
    printf("栈为空!");
  }
  else {
    LinkNode* p;
    p = L;
    printf("栈中元素如下:");
    while (p)
    {
      p = p->next;
      printf("%d", p->data);
    }
  }
}
int main() {
  Linkstack L;
  Iniststack(&L);
  Pushstack(&L);
  Popstack(&L);
  Getstack(&L);
  Printsatck(&L);
}


输出:

顺序栈完整代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#define size 5
typedef struct {
  int data[size];
  int top;
}seqstack;
//初始化操作
void InistStack(seqstack &S) {
  S.top = -1;
}
//判空操作
void IsEmpty(seqstack& S)
{
  if (S.top == -1)
    printf("目前栈为空!\n");
}
//入栈操作
void Pushstack(seqstack& S)
{
  if (S.top == size)
    printf("栈满,拒绝元素继续入栈!");
  else {
    printf("请依次输入你要入栈的元素:\n");
    int x,i;
    for (i = 0; i < size; i++) {
      scanf("%d", &x);
      S.top = S.top + 1;
      S.data[S.top] = x;
      printf("入栈成功!\n");
    }
  }
}
//读取栈顶元素
void Getstack(seqstack& S) {
  if (S.top == -1) {
    printf("栈为空,没有元素输出!");
  }
  {
    printf("当前栈顶元素为:");
    printf("%d\n", S.data[S.top]);
  }
}
//输出栈中元素
void Printstack(seqstack& S) {
  if (S.top == -1) {
    printf("栈为空,没有元素输出!");
  }
  else {
    printf("栈中元素如下:");
    for (int i = 0; i < size; i++) {
      printf("%d", S.data[i]);
    }
    printf("\n");
  }
}
//出栈
int  PopStack(seqstack& S) {
  if (S.top == -1) {
    printf("栈为空,没有元素输出!");
  }
  {
    printf("当前栈顶元素为:");
     return S.data[S.top];
     S.top = S.top - 1;
  }
}
//删除栈顶元素
int Deletestack(seqstack& S) {
  if (S.top == -1) {
    printf("栈为空!\n");
  }
  else
  {
    int e;
    for (int i = 0; i < size; i++) {
      e = S.data[S.top];
      S.top = S.top - 1;
      return e;
    }
    printf("所有元素已被删除!\n");
  }
}
//清栈
void Clearstack(seqstack& S) {
  S.top = -1;
  printf("栈已经被清空!\n");
}
int main() {
  seqstack S;
  int x;
  InistStack(S);
  IsEmpty(S);
  Pushstack(S);
  Getstack(S);
  Printstack(S);
  /*x=PopStack(S);*/
  Deletestack(S);
  Clearstack(S);
  Printstack(S);
}

输出:

相关文章
|
19天前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
102 14
|
25天前
|
存储 算法 程序员
C 语言递归算法:以简洁代码驾驭复杂逻辑
C语言递归算法简介:通过简洁的代码实现复杂的逻辑处理,递归函数自我调用解决分层问题,高效而优雅。适用于树形结构遍历、数学计算等领域。
|
23天前
|
C语言
【C语言】全局搜索变量却找不到定义?原来是因为宏!
使用条件编译和 `extern` 来管理全局变量的定义和声明是一种有效的技术,但应谨慎使用。在可能的情况下,应该优先考虑使用局部变量、函数参数和返回值、静态变量或者更高级的封装技术(如结构体和类)来减少全局变量的使用。
31 5
|
24天前
|
编译器 C语言
【C语言】宏定义在 a.c 中定义,如何在 b.c 中使用?
通过将宏定义放在头文件 `macros.h` 中,并在多个源文件中包含该头文件,我们能够在多个文件中共享宏定义。这种方法不仅提高了代码的重用性和一致性,还简化了维护和管理工作。本文通过具体示例展示了如何定义和使用宏定义,帮助读者更好地理解和应用宏定义的机制。
42 2
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
53 5
|
27天前
|
存储 算法 C语言
C语言中常见的字符串处理技巧,包括字符串的定义、初始化、输入输出、长度计算、比较、查找与替换、拼接、截取、转换、遍历及注意事项
本文深入探讨了C语言中常见的字符串处理技巧,包括字符串的定义、初始化、输入输出、长度计算、比较、查找与替换、拼接、截取、转换、遍历及注意事项,并通过案例分析展示了实际应用,旨在帮助读者提高编程效率和代码质量。
80 4
|
1月前
|
存储 安全 物联网
C语言物联网开发之设备安全与代码可靠性隐患
物联网设备的C语言代码安全与可靠性至关重要。一是防范代码安全漏洞,包括缓冲区溢出和代码注入风险,通过使用安全函数和严格输入验证来预防。二是提高代码跨平台兼容性,利用`stdint.h`定义统一的数据类型,并通过硬件接口抽象与适配减少平台间的差异,确保程序稳定运行。
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
57 1
|
23天前
|
存储 C语言 开发者
【C语言】字符串操作函数详解
这些字符串操作函数在C语言中提供了强大的功能,帮助开发者有效地处理字符串数据。通过对每个函数的详细讲解、示例代码和表格说明,可以更好地理解如何使用这些函数进行各种字符串操作。如果在实际编程中遇到特定的字符串处理需求,可以参考这些函数和示例,灵活运用。
48 10
|
23天前
|
存储 程序员 C语言
【C语言】文件操作函数详解
C语言提供了一组标准库函数来处理文件操作,这些函数定义在 `<stdio.h>` 头文件中。文件操作包括文件的打开、读写、关闭以及文件属性的查询等。以下是常用文件操作函数的详细讲解,包括函数原型、参数说明、返回值说明、示例代码和表格汇总。
43 9