数据结构与算法(C语言版)P5---栈

1、栈

1.1、栈的概念及结构

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

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

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

在这里插入图片描述

栈(stack)是一种特殊的线性表,是限定仅在一端(通常是表尾)进行插入和删除操作的线性表。

在这里插入图片描述

栈是仅在表尾进行插入、删除操作的线性表。

表尾(即an端)称为栈顶Top;表头(即a1端)称为栈底Base。

例如:

栈 S = (a1,a2,a3…an)

总结:表尾对应栈顶,表头对应栈底。

栈有两种:数组栈链表栈。

在这里插入图片描述

在这里插入图片描述

链式栈:

  • 如果是用尾做栈顶,尾插尾删,要设计成双向链表,否则删除数据效率低。
  • 如果是用头做栈顶,头插头删,要设计成单向链表。

两种都可以,非要选一种,数组栈结构稍微好一点。

数组栈存放数据的方式:

在这里插入图片描述

链式栈存放数据的方式:

  • 尾做栈顶:

    在这里插入图片描述

  • 头做栈顶

    在这里插入图片描述

总结:栈和队列时线性表的子集,是插入金额删除位置受限的线性表。

1.2、栈的思考题

问:假设与3个元素a,b,c。入栈顺序是a,b,c。

则它们的出栈出栈顺序有几种可能?

答案:5种可能。下面解释:

(1)c、b、a。原因:入栈顺序为a、b、c,然后依次取出。

(2)a、b、c。原因:a先入栈,然后出栈,之后b入栈,b出栈,最后c入栈,c出栈。

(3)a、c、b。原因:a先入栈,然后出栈,之后b、c入栈,最后,c、b出栈。

(4)b、a、c。原因:a、b先入栈,之后b、a出栈,最后c入栈,c出栈。

(5)b、c、a。原因:a、b先入栈,之后b出栈,然后c入栈,最后c、a出栈。

2、顺序栈的实现(用数组实现)

栈主要有以下几个接口函数

  • 初始化栈(StackInit)
  • 销毁(StackDestroy)
  • 栈顶插入数据(StackPush)
  • 栈顶删除数据(StackPop)
  • 取栈顶数据(StackTop)
  • 统计栈种元素个数(StackSize)
  • 判断栈是否为空(StackEmpty)

2.1、定义结构体和main函数

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int SLDataType;typedef struct Stack
{SLDataType* a;int top;        //栈顶int capacity;
};

main函数

#include "stack.h"int main()
{ST st;return 0;
}

2.2、初始化栈

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

2.3、销毁

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

2.4、栈顶插入数据

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

2.5、栈顶删除数据

//栈顶删除数据
void StackPop(ST* ps)
{assert(ps);assert(ps->top > 0);ps->top--;
}

2.6、取栈顶元素

//取栈顶数据
SLDataType StackTop(ST* ps)
{assert(ps);assert(ps->top > 0);return ps->a[ps->top - 1];
}

2.7、统计栈中元素个数

//统计栈中元素个数
int StackSize(ST* ps)
{return ps->top;
}

2.8、判断栈是否为空

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

3、全代码展示

这里使用三个文件:

  • stack.h:用于结构体、各种函数接口的声明
  • stack.c:用于各种函数接口的定义。
  • test.c:用于创建链表,实现链表。

3.1、stack.h

#pragma once#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);

3.2、stack.c

#include "stack.h"void StackInit(ST* ps)
{assert(ps);ps->a = NULL;//初始化时,top给的是0,意味着top指向栈顶数据的下一个。//初始化时,top给的是-1,意味着top指向栈顶数据。ps->top = 0;ps->capacity = 0;
}
void StackDestroy(ST* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->top = 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 StackPop(ST* ps)
{assert(ps);assert(ps->top > 0);ps->top--;
}STDataType StackTop(ST* ps)   //取栈顶的数据
{assert(ps);assert(ps->top > 0);return ps->a[ps->top-1];
}int StackSize(ST* ps)       //统计栈里面有多少个数据
{return ps->top;
}bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}

3.3、test.c

#include "stack.h"void TestStack()
{ST st;StackInit(&st);StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);StackPush(&st, 5);StackPush(&st, 6);//遍历栈元素while (!StackEmpty(&st)){printf("%d ", StackTop(&st)); StackPop(&st);}StackDestroy(&st);
}int main()
{TestStack();return 0;
}

4、链栈的实现(用链表实现栈)

链栈的实现(用链表实现栈)

前面说过链栈的实现有两种方式:

  • 链表头做栈顶,这样只需要单链表即可。
  • 链表尾做栈顶,这样需要双向链表。

这里我们就以单链表的形式实现链表。

以单链表的形式实现链表,链表的结构如下:
在这里插入图片描述

这个样的链表有如下特性:

  • 链表的头指针就是栈顶。
  • 不需要头结点。
  • 基本不存在栈满的情况(需要结点就立即申请即可)。
  • 空栈相当于头指针指向NULL。
  • 插入和删除仅在栈顶处执行。

栈主要是用数组实现,链栈稍微少一点,所以这里直接放全代码,不在一个接口一个接口的分析了。
以下是个人写的版本

4.1、stack.h

#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int SLDataType;typedef struct Stack
{struct Stack* Next; SLDataType data;
}ST;//销毁
void StackDestory(ST** pps);//插入数据
void StackPush(ST** pps, SLDataType x);//删除数据
void StackPop(ST** pps);//取栈顶元素
SLDataType StackTop(ST** pps);//统计栈里面有多少个数据
int StackSize(ST** pps);//判断栈是否为空
bool StackEmpty(ST** pps);

4.2、stack.c

#include "stack.h"//插入数据
void StackPush(ST** pps, SLDataType x)
{//先扩容ST* newnode = (ST*)malloc(sizeof(ST));if (newnode == NULL){printf("malloc fail\n");exit(-1);}newnode->data = x;newnode->Next = *pps;*pps = newnode;
}//销毁
void StackDestory(ST** pps)
{ST* cur = *pps;while (cur){ST* next = cur->Next; free(cur);cur = next;}*pps = NULL;
}//删除栈顶
void StackPop(ST** pps)
{assert(*pps != NULL);ST* next = (*pps)->Next;free(*pps);*pps = next;
}//取栈顶元素
SLDataType StackTop(ST** pps)
{assert(*pps != NULL);return (*pps)->data;
}//统计栈里面有多少个数据
int StackSize(ST** pps)
{assert(*pps != NULL);int count = 0;ST* cur = *pps;while (cur){count++;cur = cur->Next;}return count;
}//判断栈是否为空
bool StackEmpty(ST** pps)
{assert(pps);return *pps == NULL;
}

4.3、test.c

#include "stack.h"int main()
{ST* st = NULL;StackPush(&st, 1);StackPush(&st, 2);StackPush(&st, 3);StackPush(&st, 4);StackPush(&st, 5);StackPush(&st, 7);ST** cur = &st;while (*cur){st = *cur;printf("%d ", StackTop(&st));StackPop(&st);}StackDestory(&st);return 0;
}

5、栈与递归

递归的定义:

  • 若一个对象部分的包含它自己,或用它自己给自己定义,则称这个对象是递归的。
  • 若一个过程直接的或间接的调用自己,则称这个过程是递归的过程。

以下三种情况常常用到递归方法

  • 递归定义的数学函数。
    • 阶乘函数。
    • 斐波那契数列。
  • 具有递归特性的数据结构。
    • 二叉树。
    • 广义表。
  • 可递归求解的问题。
    • 迷宫问题。
    • Hanoi塔问题。

递归问题————使用分治法求解

__分治法:__对于一个较为复杂的问题,能够分解成几个相对简单的且解法相同或类似的子问题来求解。

使用分治法的三个条件:

  • 能将一个问题转变为一个新问题,而新问题与原问题的解法相同或类同,不同的仅是处理的对象,且这些对象是变化有规律的。
  • 可以通过上述转化而使问题简化。
  • 必须有一个明确的递归出口,或称为递归边界。

我们再来分析函数递归掉调用过程:

调用前,系统完成:

  • 将实参,返回地址等传递给被调用函数。
  • 为被调用函数的局部变量分配存储区。
  • 将控制转移到被调用函数。

调用后,系统完成:

  • 保存被调用函数的计算结果。
  • 释放被调用函数的数据区。
  • 依照被调用函数保存的返回地址将控制转移到调用函数。

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

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

相关文章

FPGA原理与结构(12)——FIFO IP核原理学习

系列文章目录&#xff1a;FPGA原理与结构&#xff08;0&#xff09;——目录与传送门 一、FIFO概述 1、FIFO的定义 FIFO是英文First-In-First-Out的缩写&#xff0c;是一种先入先出的数据缓冲器&#xff0c;与一般的存储器的区别在于没有地址线&#xff0c; 使用起来简单&…

BD就业复习第一天

hive 1.分区分桶 在Hive中&#xff0c;分区&#xff08;Partition&#xff09;和分桶&#xff08;Bucketing&#xff09;都是用于数据管理和查询性能优化的技术。它们有不同的用途和特点。 分区&#xff08;Partition&#xff09;&#xff1a; 定义&#xff1a;分区是将数据…

Hexo在多台电脑上提交和更新

文章目录 1.博客搭建2.创建一个新文件夹new&#xff0c;用于上传hexo博客目录3.github上创建hexo分支并设置为默认分支创建hexo分支将hexo分支设置为默认分支 4.进入新建的文件夹中git clone&#xff0c;再上传相关文件至hexo分支1.clone下来的文件夹内应该有个.gitignore文件&…

Springboot部署服务器项目上线

第一步&#xff0c;项目打包&#xff0c;有两种方式 第一种、直接在项目根目录打开终端&#xff0c;输入以下语句即可。如下图&#xff1a; mvn clean package -DskipTests 第二种、在右侧点击 Maven选项&#xff0c;选择鼠标左键双击package选项即可。如下图&#xff1a; 两…

Python 搭建编程环境

一、搭建编程环境 1、下载python 官网&#xff1a;https://www.python.org 2、开始安装 下载安装版本&#xff0c;双击下载的安装包&#xff0c;如下&#xff1a; 步骤一&#xff1a; 步骤二&#xff1a; 步骤三&#xff1a; 安装完成后执行下面的操作&#xff0c;判断是否…

面试官:说说Vue 3.0中Treeshaking特性?举例说明一下?

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 一、是什么 二、如何做 Vue2 项目 Vue3 项目 三、作用 一、是什么 Tree shaking 是一种通过清除多余代码方式来…

974. 和可被 K 整除的子数组

974. 和可被 K 整除的子数组 C代码&#xff1a;哈希表前缀和 typedef struct{int val;int cnt;UT_hash_handle hh; } HashTable;int subarraysDivByK(int* nums, int numsSize, int k){HashTable* head NULL;HashTable* out NULL;int sum 0;int cnt 0;out (HashTable*)ma…

Nginx 的优化思路有哪些?网站的防盗链如何做?附图文说明和完整代码步骤

Nginx 的优化思路有哪些?网站的防盗链如何做?实际工作中有哪些类似的安全经验?通过代码实践一步一步实现,附图文说明和完整代码步骤 实验拓扑图: 实验步骤 1、在Centos01上安装Nginx,设置网站根目录/www使用域名www.huhu.com访问 2、在Centos02上安装DNS使用域名访问Ce…

嵌入式Linux--进程间通讯--消息队列

1.需要知道的问题&#xff1a; 1、如何创建消息队列&#xff08;A\B使用同一个队列通信&#xff09; 2、如何加消息到队列&#xff08;队列是链表&#xff09; 3、如何从队列拿到消息 消息队列&#xff1a; 消息队列&#xff0c;是消息的链接表&#xff0c;存放在内核中。一个…

【数据结构】二叉树的前序遍历(七)

题目&#xff1a;二叉树的前序遍历 题目详情&#xff1a;给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历&#xff1b; 我们先来看几个示例&#xff1a; 输入&#xff1a;root [ 1&#xff0c;null&#xff0c;2&#xff0c;3 ] 输出&#xff1a;[ 1&#xf…

爬虫 — Js 逆向案例二微信公众平台登录

目标网站&#xff1a;https://mp.weixin.qq.com/ 需求&#xff1a;找到密码加密的过程&#xff0c;进行加密 案例分析 1、抓到向服务器发请求的数据包&#xff0c;输入错误的账号和密码 2、找到加密字段 pwd 如果 Search 里面数据太多&#xff0c;也可以在 Initiator 里面查找…

LVS+Haproxy

LVSHaproxy 一、Haproxy简介1.1、Haproxy应用分析1.2、Haproxy的特性1.3、常见负载均衡策略1.4、LVS、Haproxy、Nginx区别1.5、 Haproxy的优点1.6、常见的Web集群调度器 二、Haproxy部署实例四、日志定义优化 一、Haproxy简介 Haproxy 是一个使用C语言编写的自由及开放源代码软…

vue项目搭建

一、vue项目搭建&#xff08;如遇问题依据不同情况动态解决搭建过程中的问题&#xff09; VUE官网链接:https://cli.vuejs.org/. node.js下载链接: https://nodejs.cn/download/ 在PATH中配置node的环境变量&#xff0c;node自带npm和npx C:\workSpace>node -v v18.17.0 C…

epoll的并发服务器(TCP服务器与客户端通信)

服务器&#xff1a; #include<myhead.h> #define IP "192.168.250.100" #define PORT 8888 /* typedef union epoll_data {void *ptr;int fd;uint32_t u32;uint64_t u64; } epoll_data_t;struct epoll_event {uint32_t events; …

计算机视觉与深度学习-图像分割-视觉识别任务02-目标检测-【北邮鲁鹏】

目录标题 参考目标检测定义深度学习对目标检测的作用单目标检测多任务框架多任务损失预训练模型姿态估计 多目标检测问题滑动窗口&#xff08;Sliding Window&#xff09;滑动窗口缺点 AdaBoost&#xff08;Adaptive Boosting&#xff09;参考 区域建议 selective search 思想慢…

Spring Cloud Eureka:服务注册与发现

&#x1f497;wei_shuo的个人主页 &#x1f4ab;wei_shuo的学习社区 &#x1f310;Hello World &#xff01; Spring Cloud Eureka&#xff1a;服务注册与发现 Spring Cloud Eureka是Spring Cloud生态系统中的一个组件&#xff0c;它是用于实现服务注册与发现的服务治理组件。在…

pytorch迁移学习训练图像分类

pytorch迁移学习训练图像分类 一、环境配置二、迁移学习关键代码三、完整代码四、结果对比 代码和图片等资源均来源于哔哩哔哩up主&#xff1a;同济子豪兄 讲解视频&#xff1a;Pytorch迁移学习训练自己的图像分类模型 一、环境配置 1&#xff0c;安装所需的包 pip install …

【ArcGIS】基本概念-矢量空间分析

栅格数据与矢量数据 1.1 栅格数据 栅格图是一个规则的阵列&#xff0c;包含着一定数量的像元或者栅格 常用的栅格图格式有&#xff1a;tif&#xff0c;png&#xff0c;jpeg/jpg等 1.2 矢量数据 矢量图是由一组描述点、线、面&#xff0c;以及它们的色彩、位置的数据&#x…

堆内存与栈内存

文章目录 1. 栈内存2. 堆内存3. 区别和联系参考资料 1. 栈内存 栈内存是为线程留出的临时空间 每个线程都有一个固定大小的栈空间&#xff0c;而且栈空间存储的数据只能由当前线程访问&#xff0c;所以它是线程安全的。栈空间的分配和回收是由系统来做的&#xff0c;我们不需…

如何玩转CSDN AI工具集

前言 人工智能生成内容&#xff08;AIGC&#xff09;是当下最具有前景的技术领域之一。AI能够以惊人的速度和准确度生成各种类型的内容&#xff0c;完成文章翻译、代码生成、AI对话、插图创作等工作&#xff0c;带来了许多令人兴奋的机遇。 本文将介绍CSDN AI工具集的基本使用…