【C 数据结构】栈

news2024/12/23 23:21:49

文章目录

  • 【 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=aladdin
    s->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.coloradmin.cn/o/1597658.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

牛客网刷题 :BC50 你是天才吗

描述 据说智商140以上者称为天才&#xff0c;KiKi想知道他自己是不是天才&#xff0c;请帮他编程判断。输入一个整数表示一个人的智商&#xff0c;如果大于等于140&#xff0c;则表明他是一个天才&#xff0c;输出“Genius”。 输入描述&#xff1a; 多组输入&#xff0c;每…

【opencv】示例-videocapture_microphone.cpp 使用OpenCV库实现的音频捕获

#include <opencv2/core.hpp> // 包含OpenCV核心功能头文件 #include <opencv2/videoio.hpp> // 包含OpenCV视频输入输出头文件 #include <opencv2/highgui.hpp> // 包含OpenCV高层GUI头文件 #include <iostream> // 包含标准输入输出流头文件using na…

SS3D翻译

SS3D AbstractIntroductionRelated WorkFully-Supervised 3D Object DetectionWeakly/Semi-Supervised 3D Object DetectionSparsely-Supervised 2D Object Detection MethodOverall FrameworkArchitecture of DetectorMissing-Annotated Instance Mining Module 缺失注释实例挖…

leetcode-链表中间节点

876. 链表的中间结点 题目 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&#xff1a;链表只有一个中间…

基于STM32的RFID智能门锁系统

本文针对RFID技术&#xff0c;着重研究了基于单片机的智能门锁系统设计。首先&#xff0c;通过链接4*4按键模块与主控STM32&#xff0c;实现了多种模式&#xff0c;包括刷卡开锁、卡号权限管理、密码开锁、修改密码、显示实时时间等功能。其次&#xff0c;采用RC522模块与主控S…

GitHub登录收不到邮箱验证码

由于长时间没有登录GitHub&#xff0c;浏览器可能清除了相应的cookie信息&#xff0c;所以需要对应绑定邮箱进行验证&#xff0c;但因为邮箱长时间没有收到验证码&#xff0c;所以给到以下一种可能解决的方法&#xff1a; 需要输入验证码进行验证 我们可以打开QQ邮箱&#xff0…

Wpf 使用 Prism 实战开发Day19

待办事项功能页面完善以及优化 概要&#xff1a; 由于待办事项功能页&#xff0c;数据已正常渲染出来了。但页面新增&#xff0c;查询&#xff0c;修改&#xff0c;删除等功能还未实现。本章节来实现页面的请求后台实现CURD&#xff08;增删改查&#xff09; 一.待办事项查询…

UE5不打包启用像素流 ubuntu22.04

首先查找引擎中像素流的位置&#xff1a; zkzk-ubuntu2023:/media/zk/Data/Linux_Unreal_Engine_5.3.2$ sudo find ./ -name get_ps_servers.sh [sudo] zk 的密码&#xff1a; ./Engine/Plugins/Media/PixelStreaming/Resources/WebServers/get_ps_servers.sh然后在指定路径中…

分布式锁-redission锁的MutiLock原理

5.5 分布式锁-redission锁的MutiLock原理 为了提高redis的可用性&#xff0c;我们会搭建集群或者主从&#xff0c;现在以主从为例 此时我们去写命令&#xff0c;写在主机上&#xff0c; 主机会将数据同步给从机&#xff0c;但是假设在主机还没有来得及把数据写入到从机去的时…

Java是如何实现跨平台的

Java语言的跨平台能力是其最显著的特性之一&#xff0c;它允许开发者编写一次代码&#xff0c;然后在不同的操作系统上无需修改即可运行。。 Java通过字节码和Java虚拟机&#xff08;JVM&#xff09;实现跨平台 (图源网络) 主要依赖于以下几个关键组件&#xff1a; 字节码&am…

Visual studio项目默认“Header Files”、“Source Files”等过滤器消失后展开的方法。

使用Visual Studio进行项目开发创建默认工程的解决方案资源管理器里查看项目文件&#xff0c;所有的文件是按照其所属的类型自动归类&#xff0c;例如&#xff1a;.h头文件自动划归到Header Files文件夹&#xff0c;.cpp文件自动划归到Source Files文件夹下&#xff0c;如下图所…

小米SU7的防晒秘籍

在春日渐暖的日子里&#xff0c;夏天悄然而至。大家有没有从衣柜深处翻出夏衣和防晒装备&#xff0c;来迎接夏日阳光的“偏爱”呢&#xff1f; 深知防晒烦恼的小米&#xff0c;在小米SU7的设计中也充分考虑了汽车防晒这一痛点&#xff0c;采用前风挡三层镀银、天幕双层镀银、四…

vite - WebAssembly入门

1. 初始化 vite 项目 1.1 安装 nvm&#xff08;可选&#xff09; brew update brew install nvm在 ~/.zshrc 添加 export NVM_DIR~/.nvm source $(brew --prefix nvm)/nvm.sh执行如下命令 source ~/.zshrc1.2 安装 node nvm install nodenvm ls -> …

非线性特征曲线线性化插补器(CODESYS 完整ST代码)

1、如何利用博途PLC和信捷PLC实现非线性特征曲线的线性化可以参考下面文章链接: 非线性特征曲线线性化(插补功能块SCL源代码+C代码)_scl直线插补程序-CSDN博客文章浏览阅读382次。信捷PLC压力闭环控制应用(C语言完整PD、PID源代码)_RXXW_Dor的博客-CSDN博客闭环控制的系列文章…

【QT+QGIS跨平台编译】181:【QGIS+Qt跨平台编译】—【错误处理:找不到_DEBUGA】

点击查看专栏目录 文章目录 一、找不到_DEBUGA二、原因分析三、错误处理 一、找不到_DEBUGA 报错信息&#xff1a; 二、原因分析 采用了非UNICODE&#xff1a; DEFINES - UNICODE没法识别 _DEBUGA 但可以识别 _DEBUG 三、错误处理 修改 _DEBUGA 为 _DEBUG

轻量级的Spring Cloud Gateway实践,实现api和websocket转发

当国内大部分都是粘贴复制一些重型框架时&#xff0c;有没有人会想到&#xff0c;我们自己做一个小项目&#xff0c;几个小的Spring boot的项目时&#xff0c;我们是否还需要按部就班的用我们公司中用到的Nacos&#xff0c;这种冗余且调配复杂的组件呢&#xff1f; 不是本人说…

学习STM32第十四天

软件SPI读写W25Q64 一、简介 对W25Q64模块进行读写操作时&#xff0c;输出引脚配置为推挽输出&#xff0c;输入引脚配置为浮空或上拉输入。时钟、主机输出和片选都是输出引脚&#xff0c;主机输入是输入引脚。SPI协议是通过命令和数据进行通信&#xff0c;在硬件中使用移位寄…

【练习】二分查找

1、704 &#xff08;1&#xff09;题目描述 &#xff08;2&#xff09;代码实现 package com.hh.practice.leetcode.array.demo_02;public class BinarySearch_704 {public int search(int[] nums, int target) {int i 0,j nums.length -1;while (i < j){int mid (ij) &…

ansible-tower连接git实现简单执行playbook

前提&#xff1a;安装好ansible-tower和git&#xff0c;其中git存放ansible得剧本 其中git中得内容为&#xff1a; --- - name: yjxtesthosts: yinremote_user: rootgather_facts: noroles:- testroles/test/tasks/main.yml #文件内容 --- #- name: Perform Test Task # tas…

【论文笔记】PointMamba: A Simple State Space Model for Point Cloud Analysis

原文链接&#xff1a;https://arxiv.org/abs/2402.10739 1. 引言 基于Transformer的点云分析方法有二次时空复杂度&#xff0c;一些方法通过限制感受野降低计算。这引出了一个问题&#xff1a;如何设计方法实现线性复杂度并有全局感受野。 状态空间模型&#xff08;SSM&…