当前位置: 首页 > news >正文

「栈」实现LIFO栈(先进后出栈|堆栈|stack)的功能 / 手撕数据结构(C++)

概述

,是一种基本的数据结构,也是一种数据适配器。它在底层上以链表方法或动态数组方法实现。

队列的显著特点是他的添加元素与删除元素操作:先加入的元素总是被先弹出。

一个队列应该应该是这样的:

          --------------STACK-------------———— ←-- ———— ←-- ———— ←-- ————  ←-- push()T1       T2       T3       T4  ———— --→ ———— --→ ———— --→ ————  --→ pop()  --------------------------------bottom                      top()

Tn代表该元素被加入到队列的次序。 

一个队列有以下三种基本行为: 

top()表示对队列头元素的访问操作。如得到元素T4。 

pop()表示对队列头元素的弹出操作。我们弹出T4

          --------------STACK-------------———— ←-- ———— ←-- ————      ←-- push()T1       T2       T3    ———— --→ ———— --→ ————      --→ pop()  --------------------------------bottom                      top()

现在T3成为栈顶元素。 

push()表示对队列尾部压入新元素。我们压入T5

          --------------STACK-------------———— ←-- ———— ←-- ———— ←-- ————  ←-- push()T2       T3       T4       T5  ———— --→ ———— --→ ———— --→ ————  --→ pop()  --------------------------------bottom                      top()

 现在T5成为栈顶元素。

接下来我们通过封装stack类,实现栈的一些基本功能 。(Code和测试案例附后) 

考虑到在实现队列功能的文章中我们已经使用了链表,这次我们使用动态数组来实现栈。队列详见:「队列」实现FIFO队列(先进先出队列|queue)的功能 / 手撕数据结构(C++)

 由于我们的动态数组实现的很完整,这使得本文章在代码部分基本不怎么需要讲解了。数组详见:「数组」实现动态数组的功能 / 手撕数据结构(C++)

命名空间

C++有自己的std命名空间下的stack,为了进行区分,封装一个自己的动态数组命名空间custom_stack。

使用namespace关键字封装,使用时可以声明using namespace custom_stack;在作用域内声明,或custom_stack::局部声明。

namespace custom_stack{...
}//作用域内声明
using namespace custom_stack;//局部声明
custom_stack::...

成员变量

template <typename T>泛型,作为数据适配器,他的数据单位应该是任意一种类型,此时暂用T表示,至于T为何物将在实例化时以<>告知。

定义class类queue,封装一个成员变量:custom_dynamic_array命名空间下的array<T>示例化val动态数组。

数组内部详见「数组」实现动态数组的功能 / 手撕数据结构(C++)

我们使用数组来模拟栈:

栈顶是数组的最后一个元素,入栈即是在数组尾部加入新元素,出栈即是将放弃最后一个元素然后前移。

namespace custom_stack {template<typename T>class stack {private:custom_dynamic_array::array<T> val;public:...
}

创建销毁

默认构造stack(int num = 5),接收一个num,num默认是5。为数组开为5个元素的空间。

复制构造stack(const stack& another),传入val执行复制构造。

移动构造stack(stack&& another),移动构造详见:「数组」实现动态数组的功能 / 手撕数据结构(C++)

复制赋值运算符stack& operator=(const stack& another),类似复制构造。

移动赋值运算符stack& operator=(stack&& another),类似移动构造。

由编译器给出析构函数。析构函数的内部会调用底层动态数组array的析构函数。

        stack(int num = 5) :val(num) {};stack(const stack& another): val(another.val) {};stack(stack&& another) :val(std::move(another.val)) {};stack& operator=(const stack& another) {val = another.val;}stack& operator=(stack&& another) {if (this == &another)return *this;val = std::move(another.val);}

数据控制

获取长度size_t size(),返回array类型的val内部的val_size。

判断为空bool empty(),返回array类型的val内部的val_size ? false : true。

队顶压入void push(T&& elem),在数组尾加入新元素。(T&&作为万能引用,此处不表)

队顶弹出void pop(),数组尾前移。(数组的接口函数内部使用assert断言数组不能为空)

内部交换void swap(stack& another),直接交换底层数组。

        bool empty()const{return val.empty();}size_t size()const{return val.size();}void push(T&& elem) {val.push_back(std::forward<T>(elem));}void pop() {val.pop_back();}void swap(stack& another) {val.swap(another.val);}

数据访问

访问栈顶const T& top(),断言数组长度大于0后,返回数组的尾元素。

		const T& top()const {assert(val.size() > 0);return val[val.size() - 1];}

Code

*注意*:笔者引入了位于其他位置的array头文件。

#pragma once
#include <custom\array.h>
namespace custom_stack {template<typename T>class stack {private:custom_dynamic_array::array<T> val;public:stack(int num = 5) :val(num) {};stack(const stack& another): val(another.val) {};stack(stack&& another) :val(std::move(another.val)) {};stack& operator=(const stack& another) {val = another.val;}stack& operator=(stack&& another) {if (this == &another)return *this;val = std::move(another.val);}bool empty(){return val.empty();}size_t size(){return val.size();}void push(T &&elem) {val.push_back(std::forward<T>(elem));}void pop() {val.pop_back();}void swap(stack& another) {val.swap(another.val);}const T& top() {assert(val.size() > 0);return val[val.size() - 1];}};
}

测试 

#include <iostream>
#include "stack.h"
using namespace std;
int main() {custom_stack::stack<char>stk1;std::cout << "---------------test1---------------" << std::endl;for (int i = 0; i < 10; i++) {stk1.push(i + 'a');cout << char(i + 'a');}cout << endl;std::cout << "-----------------------------------" << std::endl;std::cout << "---------------test2---------------" << std::endl;custom_stack::stack<char>stk2(stk1);for (int i = 0; i < 10; i++) {cout << stk2.top();stk2.pop();}std::cout << endl;std::cout << "-----------------------------------" << std::endl;return 0;
}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • DALL-E 2:当AI遇上画笔,艺术界的“魔术师”横空出世!
  • 电脑屏幕录制工具分享5款,附上详细电脑录屏教程(2024全新)
  • 【Java】 深入了解 Java util 包中的 add() 方法
  • Elasticsearch 创建索引库指南
  • ERROR 1698 (28000): Access denied for user ‘root‘@‘localhost‘
  • vulhub,docker一直启动不起来?docker配置文件错误(/etc/docker/daemon.json )
  • 前端传递ids ,gorm 删除
  • IMU助力跑步参数评估
  • 漏洞复现-Apache Kafka Clients JNDI注入漏洞 (CVE-2023-25194)
  • springboot项目搭建集成 redis/跨域/远程请求
  • 在Ubuntu上有什么命令,或者是系统文件能告诉我链接nvme ssd的pcie槽位是不是支持热插拔功能?
  • 【Cpp筑基】三、对象和类
  • g++ 编译器参数作用
  • “5G+Windows”推动全场景数字化升级:美格智能5G智能模组SRM930成功运行Windows 11系统
  • TPM管理咨询公司浅谈:Muri、Muda、Mura
  • __proto__ 和 prototype的关系
  • 【面试系列】之二:关于js原型
  • Android框架之Volley
  • axios 和 cookie 的那些事
  • Linux各目录及每个目录的详细介绍
  • mysql外键的使用
  • ng6--错误信息小结(持续更新)
  • Quartz初级教程
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • ReactNativeweexDeviceOne对比
  • Redis字符串类型内部编码剖析
  • ubuntu 下nginx安装 并支持https协议
  • 工作中总结前端开发流程--vue项目
  • 记录一下第一次使用npm
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 猫头鹰的深夜翻译:Java 2D Graphics, 简单的仿射变换
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 使用common-codec进行md5加密
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 吐槽Javascript系列二:数组中的splice和slice方法
  • 微服务入门【系列视频课程】
  • 新手搭建网站的主要流程
  • 一起参Ember.js讨论、问答社区。
  • ​批处理文件中的errorlevel用法
  • ​水经微图Web1.5.0版即将上线
  • ​油烟净化器电源安全,保障健康餐饮生活
  • #if和#ifdef区别
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (1)STL算法之遍历容器
  • (ctrl.obj) : error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“
  • (SpringBoot)第七章:SpringBoot日志文件
  • (vue)页面文件上传获取:action地址
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (微服务实战)预付卡平台支付交易系统卡充值业务流程设计
  • (一)RocketMQ初步认识
  • (译)计算距离、方位和更多经纬度之间的点
  • ***原理与防范
  • .NET 8.0 中有哪些新的变化?