【C 数据结构】栈

文章目录

  • 【 1. 基本原理 】
    • 栈的分类
  • 【 2. 动态链表栈 】
    • 2.1 双结构体实现
      • 2.1.0 栈的节点设计
      • 2.1.1 入栈
      • 2.1.2 出栈
      • 2.1.3 遍历
      • 2.1.4 实例
    • 2.2 单结构体实现
      • 2.2.0 栈的节点设计
      • 2.2.1 入栈
      • 2.2.2 出栈
      • 2.2.3 实例
  • 【 3. 顺序栈 】
    • 3.1 入栈
    • 3.2 出栈
    • 3.3 实例

【 1. 基本原理 】

  • 栈(Stack) 是一个线性的数据结构:
    • 栈只能从表的一端存取数据,另一端是封闭的;
    • 在栈中,无论是存数据还是取数据,都必须遵循"先进后出"的原则,即最先进栈的元素最后出栈。
      在这里插入图片描述
  • 栈的开口端被称为 栈顶,栈顶元素指的就是距离栈顶最近的元素;封口端被称为 栈底,栈底元素指的是位于栈最底部的元素。
    在这里插入图片描述
  • 栈的的应用
    • 网页的返回。
    • 代码中的括号匹配。
    • 进制转换。

栈的分类

  • 栈分为数组栈和链表栈:
    • 顺序栈(数组栈) 采用顺序存储结构,使用数组进行功能的模拟,实现较为快速和便利。
    • 链表栈(链栈) 采用链式存储结构,实现较为麻烦,但是其稳定不易出错。在链表栈中又分为静态链表栈 和 动态链表栈:
      • 静态链表栈 给定栈的空间大小,不允许超过存储超过给定数据大小的元素。
      • 动态链表栈 使用的是 自动创建空间 的方法进行创建,只要符合机器的硬件要求以及编译器的控制,其理论上是极大的。

【 2. 动态链表栈 】

  • 我们以链表栈的动态链表栈为例子,进行栈的设计,在后文直接以栈一名字称呼动态链表栈,这也是各类语言标准模板中栈的实现方式。
    在这里插入图片描述

2.1 双结构体实现

2.1.0 栈的节点设计

  • 我们可以设计出两个结构体:
    • 一个结构体Node表示结点,其中包含有一个data域和next指针。其中data表示数据,其可以是简单的类型(如int,double等等),也可以是复杂的结构体(struct类型);next指针表示,下一个的指针,其指向下一个结点,通过next指针将各个结点链接。
      在这里插入图片描述
    • 额外添加的另一个结构体,其包括了一个永远指向栈头的指针top和一个计数器count记录元素个数,(也可以设计成一个指针top和一个指针bottom分别指向栈头和栈尾)其主要功效就是设定允许操作元素的指针以及确定栈何时为空(count的方法是当count为0时为空,top和bottom方法就是两者指向同一个空间时为栈为空)。
      在这里插入图片描述
  • 采用的 top 和 count 组合的方法,C 实现:
//栈的结点设计
//单个结点设计,数据和下一个指针
typedef struct node     
{int data; struct node *next;
} Node;
//利用上面的结点创建栈,分为指向头结点的top指针和计数用的count
typedef struct stack    
{Node *top;int count;
} Link_Stack;

2.1.1 入栈

  • 入栈(push)操作时,我们只需要找到top所指向的空间,创建一个新的结点,将新的结点的next指针指向我们的top指针指向的空间,再将top指针转移,指向新的结点,即是入栈操作。
    在这里插入图片描述
  • C 实现:
//入栈 push
Link_Stack *Push_stack(Link_Stack *p, int elem)
{if (p == NULL)return NULL;Node *temp;temp=(Node*)malloc(sizeof(Node));temp->data = elem;temp->next = p->top;p->top = temp;p->count++;return p;
}

2.1.2 出栈

  • 出栈(pop)操作,是在栈不为空的情况下(注意一定要进行判空操作),将栈顶的元素删除,同时top指针,next向下进行移动即可的操作。
    在这里插入图片描述
  • C 实现:
//出栈 pop
Link_Stack *Pop_stack(Link_Stack *p)
{Node *temp;temp = p->top;if (p->top == NULL){printf("错误:栈为空");return p;}else{p->top = p->top->next;free(temp);p->count--;return p;}
}

2.1.3 遍历

  • 栈的遍历,由于栈的特殊性质,其只允许在一端进行操作,所以我们的遍历操作永远都是逆序的,其过程为:在栈不为空的情况下,一次从栈顶元素向下访问,直到指针指向空(即到栈尾)为结束。
  • C 实现:
//遍历栈:输出栈中所有元素
int show_stack(Link_Stack *p)
{Node *temp;temp = p->top;if (p->top == NULL){printf("");printf("错误:栈为空");return 0;}while (temp != NULL){printf("%d\t", temp->data);temp = temp->next;}printf("\n");return 0;
}

2.1.4 实例

  • C 实现:
#include <stdio.h>
#include <stdlib.h>
//栈的结点设计
//单个结点设计,数据和下一个指针
typedef struct node     
{int data; struct node *next;
} Node;
//利用上面的结点创建栈,分为指向头结点的top指针和计数用的count
typedef struct stack    
{Node *top;int count;
} Link_Stack;//创建栈
Link_Stack *Creat_stack()
{Link_Stack *p;//p = new Link_Stack;p=(Link_Stack*)malloc(sizeof(Link_Stack));if(p==NULL){printf("创建失败,即将退出程序");exit(0);}p->count = 0;p->top = NULL;return p;
}//入栈 push
Link_Stack *Push_stack(Link_Stack *p, int elem)
{if (p == NULL)return NULL;Node *temp;temp=(Node*)malloc(sizeof(Node));//temp = new Node;temp->data = elem;temp->next = p->top;p->top = temp;p->count++;return p;
}//出栈 pop
Link_Stack *Pop_stack(Link_Stack *p)
{Node *temp;temp = p->top;if (p->top == NULL){printf("错误:栈为空");return p;}else{p->top = p->top->next;free(temp);//delete temp;p->count--;return p;}
}//遍历栈:输出栈中所有元素
int show_stack(Link_Stack *p)
{Node *temp;temp = p->top;if (p->top == NULL){printf("");printf("错误:栈为空");return 0;}while (temp != NULL){printf("%d\t", temp->data);temp = temp->next;}printf("\n");return 0;
}int main()
{ //用主函数测试一下功能Link_Stack *p;p = Creat_stack();int n = 5;int input[6] = {10,20,30,40,50,60};/以依次入栈的方式创建整个栈//for(int i=0;i<n;i++){Push_stack(p, input[i]);}show_stack(p);出栈///Pop_stack(p);show_stack(p);return 0;
}

在这里插入图片描述

2.2 单结构体实现

  • 使用单结构体实现链表:
    • 在实现数据"入栈"操作时,需要将数据从链表的头部插入(单链表的头插法);
    • 在实现数据"出栈"操作时,需要删除链表头部的首元节点;

2.2.0 栈的节点设计

  • C实现:
//链表中的节点结构
typedef struct lineStack{int data;struct lineStack * next;
}lineStack;

2.2.1 入栈

  • 将元素 1、2、3、4 依次入栈,等价于将各元素采用头插法依次添加到链表中,每个数据元素的添加过程如图所示:
    在这里插入图片描述
  • C实现:
//链表中的节点结构
typedef struct lineStack{int data;struct lineStack * next;
}lineStack;
//stack为当前的链栈,a表示入栈元素
lineStack* push(lineStack * stack,int a){//创建存储新元素的节点lineStack * line=(lineStack*)malloc(sizeof(lineStack));line->data=a;//新节点与头节点建立逻辑关系line->next=stack;//更新头指针的指向stack=line;return stack;
}

2.2.2 出栈

  • 若要将元素 3 出栈,根据"先进后出"的原则,要先将元素 4 出栈,也就是从链表中摘除,然后元素 3 才能出栈:
  • C 实现:
//栈顶元素出链栈的实现函数
lineStack * pop(lineStack * stack){if (stack) {//声明一个新指针指向栈顶节点lineStack * p=stack;//更新头指针stack=stack->next;printf("出栈元素:%d ",p->data);if (stack) {printf("新栈顶元素:%d\n",stack->data);}else{printf("栈已空\n");}free(p);}else{printf("栈内没有元素");return stack;}return stack;
}

2.2.3 实例

#include <stdio.h>
#include <stdlib.h>
typedef struct lineStack{int data;struct lineStack * next;
}lineStack;
lineStack* push(lineStack * stack,int a){lineStack * line=(lineStack*)malloc(sizeof(lineStack));line->data=a;line->next=stack;stack=line;return stack;
}
lineStack * pop(lineStack * stack){if (stack) {lineStack * p=stack;stack=stack->next;printf("弹栈元素:%d ",p->data);if (stack) {printf("栈顶元素:%d\n",stack->data);}else{printf("栈已空\n");}free(p);}else{printf("栈内没有元素");return stack;}return stack;
}
int main() {lineStack * stack=NULL;stack=push(stack, 1);stack=push(stack, 2);stack=push(stack, 3);stack=push(stack, 4);stack=pop(stack);stack=pop(stack);stack=pop(stack);stack=pop(stack);stack=pop(stack);return 0;
}

在这里插入图片描述

【 3. 顺序栈 】

  • 我们通过顺序表(底层实现是数组)模拟顺序栈/数组栈。思路为:在顺序表中设定一个实时指向栈顶元素的变量(一般命名为 top),top 初始值为 -1,表示栈中没有存储任何数据元素,及栈是"空栈"。一旦有数据元素进栈,则 top 就做 +1 操作;反之,如果数据元素出栈,top 就做 -1 操作。

在这里插入图片描述

3.1 入栈

  • 比如,模拟栈存储 {1,2,3,4} 的过程。最初,栈是"空栈",即数组是空的,top 值为初始值 -1:
    在这里插入图片描述
  • 首先向栈中添加元素 1,我们默认数组下标为 0 一端表示栈底,因此,元素 1 被存储在数组 a[0] 处,同时 top 值 +1:
    在这里插入图片描述
  • 采用以上的方式,依次存储元素 2、3 和 4,最终,top 值变为 3:
    在这里插入图片描述
  • C 实现:
//元素elem进栈,a为数组,top值为当前栈的栈顶位置
int push(int* a,int top,int elem){a[++top]=elem;return top;
}

3.2 出栈

  • 将上图中的元素 2 出栈,则需要先将元素 4 和元素 3 依次出栈。当有数据出栈时,要将 top 做 -1 操作。因此,元素 4 和元素 3 出栈的过程分别如图所示:
    在这里插入图片描述
  • 上图中数组中元素的消失仅是为了方便初学者学习,其实,这里只需要对 top 值做 -1 操作即可,因为 top 值本身就表示栈的栈顶位置,因此 top-1 就等同于栈顶元素出栈。并且后期向栈中添加元素时,新元素会存储在类似元素 4 这样的旧元素位置上,将旧元素 覆盖
  • C 实现:
//数据元素出栈
int pop(int * a,int top){if (top==-1) { //防止用户做 "栈中已无数据却还要数据出栈" 的错误操作printf("空栈");return -1;}printf("弹栈元素:%d\n",a[top]);top--;return top;
}

3.3 实例

  • 实例1:直接数组法
#include <stdio.h>
//元素elem进栈
int push(int* a, int top, int elem) {a[++top] = elem;return top;
}
//数据元素出栈
int pop(int* a, int top) {if (top == -1) {printf("空栈");return -1;}printf("弹栈元素:%d\n", a[top]);top--;return top;
}
int main() {int a[100];int top = -1;top = push(a, top, 3);top = push(a, top, 1);top = push(a, top, 4);top = push(a, top, 5);top = pop(a, top);top = pop(a, top);top = pop(a, top);top = pop(a, top);top = pop(a, top);return 0;
}

在这里插入图片描述

  • 实例2:构建结构体
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define maxn 10000//结点设计
typedef struct stack{int data[maxn];int top;
}stack;//创建
stack *init(){stack *s=(stack *)malloc(sizeof(stack));if(s==NULL){printf("分配内存空间失败");exit(0);}memset(s->data,0,sizeof(s->data));//memset操作来自于库文件string.h,其表示将整个空间进行初始化//不理解可以查阅百度百科https://baike.baidu.com/item/memset/4747579?fr=aladdins->top=0;     //栈的top和bottom均为0(表示为空)return s;
}//入栈push
void push(stack *s,int data){s->data[s->top]=data;s->top++;
}//出栈pop
void pop(stack *s){if(s->top!=0){s->data[s->top]=0;  //让其回归0模拟表示未初始化即可s->top--;}
}//模拟打印栈中元素
void print_stack(stack *s){for(int n=s->top-1;n>=0;n--){printf("%d\t",s->data[n]);}printf("\n");   //习惯性换行
}int main(){stack *s=init();int input[5]={11,22,33,44,55};  //模拟五个输入数据for(int i=0;i<5;i++){push(s,input[i]);}print_stack(s);/pop(s);print_stack(s);return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/619733.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手机数据恢复工具

下载地址&#xff1a;手机数据恢复工具.zip Android/HarmonyOS 文件误删是日常使用电子设备时经常遇到的问题&#xff0c;也许一不小心就就可能会误删。 俗话说&#xff1a;数据无价&#xff0c;一但想要找回一些被删除的文件&#xff0c;就需要耗费大量的精力和财力来恢复文…

three.js捋文档的记录笔记(六):场景 几何体 材质 物体 相机 渲染器的简单理解

三维场景Scene const scene new THREE.Scene();物体形状&#xff1a;几何体 Geometry //创建一个长方体几何对象Geometry const geometry new THREE.BoxGeometry(100, 100, 100); 物体外观&#xff1a;材质Material //创建一个材质对象Material const material new THREE.M…

锂电池寿命预测 | Matlab基于BiLSTM双向长短期记忆神经网络的锂电池寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于BiLSTM双向长短期记忆神经网络的锂电池寿命预测 程序设计 完整程序和数据获取方式&#xff1a;私信博主回复Matlab基于BiLSTM双向长短期记忆神经网络的锂电池寿命预测。 参考资料 [1] h…

自编译支持CUDA硬解的OPENCV和FFMPEG

1 整体思路 查阅opencv的官方文档&#xff0c;可看到有个cudacodec扩展&#xff0c;用他可方便的进行编解码。唯一麻烦的是需要自行编译opencv。 同时&#xff0c;为了考虑后续方便&#xff0c;顺手编译了FFMPEG&#xff0c;并将其与OPENCV绑定。 在之前的博文“鲲鹏主机昇腾A…

家庭网络防御系统搭建-将NDR系统的zeek日志集成到security onion

在前面的文章中安装了zeek,这里&#xff0c;安装了securityonion&#xff0c;这里&#xff0c;本文讲述如何将zeek生成的日志发送到siem security onion之中。 所有日志集成的步骤分为如下几步&#xff1a; 日志收集配置日志发送接收日志解析配置日志展示配置 ZEEK日志收集配…

BackTrader 中文文档(十二)

原文&#xff1a;www.backtrader.com/ Visual Chart 原文&#xff1a;www.backtrader.com/docu/live/vc/vc/ 与 Visual Chart 的集成支持两者&#xff1a; 实时数据提供 实时交易 Visual Chart是完整的交易解决方案&#xff1a; 在单个平台上集成图表、数据源和经纪功能 更多…

第⑭讲:Ceph集群管理:守护进程管理、日志管理和端口号配置

文章目录 1.Ceph各组件守护进程的管理方式2.守护进程管理操作2.1.Ceph所有组件的守护进程列表2.2.重启当前主机中所有的Ceph组件2.3.重启主机中所有的Monitor组件2.4.重启指定主机的Monitor组件2.5.重启指定的OSD组件 3.Ceph的日志管理4.Ceph集群各组件的守护进程5.Ceph集群各组…

通讯录(单链表思想)

文章目录 通讯录 2.0头文件通讯录操作函数通讯录主函数 通讯录 2.0 之前分享过使用顺序表思想实现通讯录&#xff0c;现在分享使用单链表实现的通讯录&#xff0c;我们只需要规定每个链表的元素是结构体类型&#xff0c;每个结构体内存放联系人信息即可。 **不断地练习才能熟练…

医用分子筛制氧机设计规范详细介绍

随着技术的不断进步&#xff0c;医用分子筛制氧机作为现代领域的重要设备&#xff0c;其设计规范显得尤为重要。本文将详细阐述医用分子筛制氧机的设计规范&#xff0c;确保设备的性能稳定、安全可靠。 一、设计原则 医用分子筛制氧机的设计应遵循安全、高效、稳定、易维护的原…

MySQL的权限管理

MySQL的权限管理 在理解MySQL的权限管理之前&#xff0c;我们需要先了解其架构设计以及权限管理在该架构中的定位。 MySQL的架构设计 MySQL数据库系统采用了分层的架构设计&#xff0c;主要可以分为以下几个层级&#xff1a; 连接层&#xff1a;最外层&#xff0c;处理连接…

设计一个通知系统

设计的系统支持不同类型消息的发送&#xff0c;例如push消息&#xff0c;sms消息和邮箱消息&#xff0c;能够支持千万级别的发送&#xff0c;保证消息推送的幂等性。系统结构图如下&#xff1a; 系统架构图中各组件作用说明&#xff1a; device/setting/user info&#xff1a;…

Python大数据分析——一元与多元线性回归模型

Python大数据分析——一元与多元线性回归模型 相关分析概念示例 一元线性回归模型概念理论分析函数示例 多元线性回归模型概念理论分析示例 线性回归模型的假设检验模型的F检验理论分析示例 模型的T检验理论分析示例 相关分析 概念 a 正相关&#xff1b;b 负相关&#xff1b;c…