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

news2024/11/16 1:27:32

概述

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

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

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

          --------------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类val,封装一个成员变量:custom_dynamic_array命名空间下的array<T>示例化val动态数组。

*注意*:文末附有不封装array数组的实现方案。

数组内部详见「数组」实现动态数组的功能 / 手撕数据结构(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;
}

另附 

不嵌套底层array类的栈结构。

#include <cassert>
#ifndef CUSTOM_STACK
#define CSUTOM_STACK
namespace custom_stack {
	template<typename T>
	class stack {
	private:
		T* val;
		size_t val_size;
		size_t val_capacity;
	public:
		stack(int num = 1) :val_size(0),val_capacity(num) {
			val = new T[num];
		};
		stack(const stack& another) : val_size(another.val_size), val_capacity(another.val_capacity) {//拷贝构造
			val = new T[another.val_capacity];
			memcpy(val, another.val, sizeof(T) * another.val_size);
		}
		stack(stack&& another) noexcept : val_size(another.val_size), val_capacity(another.val_capacity) {//移动构造
			val = another.val;
			another.val = nullptr;
		}
		stack& operator=(const stack& another) {
			delete[]val;
			val = new T[another.val_capacity];
			memcpy(val, another.val, sizeof(T) * another.val_size);
			val_size = another.val_size;
			val_capacity = another.val_capacity;
			return *this;
		}
		stack& operator=(stack&& another) {
			if (this == &another)return *this;
			delete[]val;
			val = another.val;
			val_size = another.val_size;
			val_capacity = another.val_capacity;
			another.val = nullptr;
			return *this;
		}
		bool empty()const {
			return val_size?false:true;
		}
		size_t size()const {
			return val_size;
		}
		void reserve(const int num) {
			if (num > val_capacity) {
				T* temp = new T[num];
				memcpy(temp, val, sizeof(T) * val_size);
				delete[]val;
				val = temp;
				val_capacity = num;
			}
		}
		template<typename V>
		void push(V&& elem) {
			if (val_capacity - val_size <= 1)reserve(val_capacity * 2);
			val[val_size++] = elem;
		}
		void pop() {
			assert(val_size > 0);
			val_size--;
		}
		const T& top()const {
			assert(val_size > 0);
			return val[val_size - 1];
		}
	};
}
#endif

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

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

相关文章

UE网络同步(一) —— 一个项目入门UE网络同步之概念解释

最近在学习UE网络同步&#xff0c;发现了一个非常好的教程&#xff0c;并且附带了项目文件&#xff0c;这里从这个小项目入手&#xff0c;理解UE的网络同步 教程链接&#xff1a;https://www.youtube.com/watch?vJOJP0CvpB8w 项目链接&#xff1a;https://github.com/awforsyt…

单片机内存映射

在一些桌面程序中&#xff0c;整个内存映射是通过虚拟内存来进行管理的&#xff0c;使用一种称为内存管理单元(MMU)的硬件结构来将程序的内存映射到物理RAM。在对于 RAM 紧缺的嵌入式系统中&#xff0c;是缺少 MMU 内存管理单元的。 因此在一些嵌入式系统中&#xff0c;比如常用…

Python 设计模式之适配者模式

文章目录 从电源适配器谈起实现适配器模式的两种方式object adapterclass adapter 从电源适配器谈起 适配者模式属于结构型设计模式&#xff0c;它的目的是使不兼容的两个对象能够相互工作。 常见的生活例子&#xff1a;去欧美国家旅游时&#xff0c;由于插口和电压不一样&am…

2024年【四川省安全员B证】新版试题及四川省安全员B证模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 四川省安全员B证新版试题是安全生产模拟考试一点通总题库中生成的一套四川省安全员B证模拟考试&#xff0c;安全生产模拟考试一点通上四川省安全员B证作业手机同步练习。2024年【四川省安全员B证】新版试题及四川省安…

苍穹外卖项目DAY01

苍穹外卖项目Day01 1、软件开发整体介绍 1.1、软件开发流程 1.2、角色分工 项目经理&#xff1a;对整个项目负责&#xff0c;任务分配、把控进度产品经理&#xff1a;进行需求调研&#xff0c;输出需求调研文档、产品原型等UI设计师&#xff1a;根据产品原型输出界面效果图架…

第130天:内网安全-横向移动PTH哈希PTT 票据PTK密匙Kerberos密码喷射

环境搭建 这里这个环境继续上一篇文章搭建的环境 案例一&#xff1a;域横向移动-PTH-Mimikatz&NTLM 什么是pth&#xff1f; PTH Pass The Hash &#xff0c;通过密码散列值 ( 通常是 NTLM Hash) 来进行攻击。在域环境中&#xff0c;用户登录计算机时使用的域账号&…

Linux_Shell判断循环,函数实例,状态码-07

一&#xff1a;分支语句 结束语句等于开始语句得单词反着写 1.1 if语句 if 表达式 then逻辑处理 elif 表达式 then逻辑处理 else逻辑处理 fi 1.1.1 单支语句 1.1.2 双分支语句 1.1.3 多分支语句 1.2 case条件结构语句 1.2.1 case语法结构 case 表达式 in 值) ;; esac 1.2…

Redis14-缓存同步

目录 缓存同步策略 安装Canal 监听Canal 缓存同步策略 缓存数据同步的常见方式有三种&#xff1a; 1.设置有效期&#xff1a;给缓存设置有效期&#xff0c;到期后自动删除&#xff0c;再次查询时更新 优势&#xff1a;简单、方便缺点&#xff1a;时效性差&#xff0c;缓存…

【CPP】CPP的STL(前篇)

目录 12 STL(前篇)12.1 什么是STL12.2 string类12.2.1 什么是string12.2.2 string类的构造函数12.2.3 string类的析构函数12.2.4 string类的[]重载12.2.5 string类的迭代器的简单了解12.2.6 auto关键字12.2.7 范围for12.2.8 反向迭代器 -- reverse12.2.9 const迭代器12.2.10 CP…

superset定制化配置修改总结

1.需要想用iframe引入dashboard时&#xff0c; URL 参数可用于修改仪表板的呈现方式&#xff0c;standalone0 属性枚举描述standalone0仪表盘正常显示1顶部导航已隐藏2顶部导航 标题被隐藏3顶部导航 标题 顶级标签被隐藏show_filters0渲染没有过滤栏的仪表板1&#xff08;默…

深度学习 —— 个人学习笔记16(目标检测和边界框、目标检测数据集)

声明 本文章为个人学习使用&#xff0c;版面观感若有不适请谅解&#xff0c;文中知识仅代表个人观点&#xff0c;若出现错误&#xff0c;欢迎各位批评指正。 三十二、目标检测和边界框 import torch import matplotlib.pyplot as plt from matplotlib_inline import backend_…

Python爬虫开发:BeautifulSoup、Scrapy入门

在现代网络开发中&#xff0c;网络爬虫是一个非常重要的工具。它可以自动化地从网页中提取数据&#xff0c;并且可以用于各种用途&#xff0c;如数据收集、信息聚合和内容监控等。在Python中&#xff0c;有多个库可以用于爬虫开发&#xff0c;其中BeautifulSoup和Scrapy是两个非…

CVE-2024-38077:Windows远程桌面授权服务的‘隐形杀手’——深度剖析与紧急防护策略

文章目录 CVE-2024-38077&#xff1a;Windows远程桌面授权服务的‘隐形杀手’——深度剖析与紧急防护策略1 漏洞描述2 漏洞影响2.1 处置优先级&#xff1a;高2.2 影响版本 3 漏洞检测3.1 漏洞检测工具3.2 漏洞检测工具使用介绍3.2.1 漏洞检测工具当前支持三种方式检测3.2.2 漏洞…

常见的三个事务问题(脏读/幻读/不可重复读)

常见的三个事务问题&#xff08;脏读/幻读/不可重复读&#xff09; 脏读 脏读&#xff08;Dirty Read&#xff09;是指在一个事务中&#xff0c;读取了另一个未提交事务的数据。 具体来说&#xff0c;脏读的过程如下&#xff1a; 1. 事务A开始&#xff0c;对某一行数据进行…

C++学习笔记之数组

C学习笔记之数组 https://www.runoob.com/cplusplus/cpp-arrays.html C当中&#xff0c;数组是用于存储固定大小的相同类型元素的顺序集合 数组是整体作为一个变量&#xff0c;其中又包含多个单独变量&#xff0c;作为其元素&#xff0c;如数组变量a&#xff0c;其包含a[0]~a[…

html+css+js网页制作 淘宝首页1个页面带js

htmlcssjs网页制作 淘宝首页1个页面带js 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&…

【并查集、树的直径】P2195 HXY造公园 题解

题意 P2195 codeforces 455c&#xff0c;两道一样的题 给出一个由 n n n 个点&#xff0c; m m m 条边组成的森林&#xff0c;有 q q q 组询问,每次询问有以下两种情况 输入 o p 1 op 1 op1 时&#xff1a;给出点 x x x&#xff0c;输出点 x x x 所在的树的直径。 输…

千元不到,作为可穿戴AI设备,AI Friend真的能够取代手机吗?

在人工智能的浪潮中&#xff0c;我们见证了无数旨在提高效率和生产力的创新设备。 然而&#xff0c;Friend设备以其独特的设计理念&#xff0c;为AI设备带来了新的定义——一个永远在线的伴侣&#xff0c;一个情感的稳定器。 一、Friend的设计理念 Friend设备的设计初衷并非追…

vscode的C/C++环境配置和调试技巧

目录 1.背景 2.下载编译器 3.配置环境变量 4.安装C/C插件 5.写C语言代码并且编译成功 5.1文件操作 5.2对于两个窗口的解释 5.3C语言编译环境配置 6.创建执行文件 7.编译运行过程 8.写其他的代码的解决方案一 9.写其他的代码的解决方案二 10.同时编译多个.c文件 10…

Qt 中实现异步散列器

【写在前面】 在很多工作中&#xff0c;我们需要计算数据或者文件的散列值&#xff0c;例如登录或下载文件。 而在 Qt 中&#xff0c;负责这项工作的类为 QCryptographicHash。 关于 QCryptographicHash&#xff1a; QCryptographicHash 是 Qt 框架中提供的一个用于生成加密散列…