基础数据结构---栈

news2024/12/19 22:54:54

顺序表实现

一、栈类的声明

栈是一种特殊的线性表,可以由顺序表来实现,也可以由链表来实现,这节课,我们采用顺序表来实现栈。

#include <iostream>

#include <stdexcept>



using namespace std;



template<typename T> // (1)

class Stack { // (2)

private:

T *data; // (3)

int size; // (4)

int capacity; // (5)

void resize(); // (6)

public:

Stack() : data(new T[10]), size(0), capacity(10) {} // (7)

~Stack(); // (8)

void push(T element); // (9)

T pop(); // (10)

T top() const; // (11)

int getSize() const; // (12)

};

(1) template<typename T> 这是一个模板声明,表明 Stack 类是一个通用的模板类,可以用于存储任何类型的元素 T。

(2) 这是 Stack 类的声明,它表示一个栈的数据结构。

(3) data 是一个私有成员变量,用于存储栈中的元素。它是一个指向类型为 T 的指针。

(4) size 是一个私有成员变量,用于记录栈中元素的数量。

(5) capacity 这是一个私有成员变量,用于记录栈的容量。

(6) resize() 是一个私有成员函数,用于在栈容量不足时进行扩容。

(7) Stack() 是构造函数,用于初始化栈的成员变量。它创建一个新的栈,并分配一个容量为 10 的数组来存储元素。

(8) ~Stack() 是析构函数,用于释放栈所占用的内存。

(9) void push(T element) 这是一个公共成员函数,用于将一个新元素压入栈顶。

(10) T pop() 是一个公共成员函数,用于从栈顶弹出一个元素。

(11) T top() 是一个公共成员函数,用于获取栈顶的元素,但不弹出它。

(12) int getSize() 是一个公共成员函数,用于获取栈中元素的数量。

二、栈的扩容

template<typename T>

void Stack<T>::resize() { // (1)

int newCapacity = capacity * 2; // (2)

T *newData = new T[newCapacity]; // (3)

for (int i = 0; i < size; i++) {

newData[i] = data[i]; // (4)

}

delete[] data; // (5)

data = newData; // (6)

capacity = newCapacity; // (7)

}

resize 函数用于在栈的容量不足时进行扩容操作。它创建了一个新的更大的数组,并将旧数组的元素复制到新数组中,然后更新栈的指针和容量。这样可以确保栈能够容纳更多的元素,而不会发生溢出。

(1) template<typename T> 是模板声明的一部分,表示 resize 函数是一个通用的模板函数,可以用于处理任何类型的元素 T。

(2) 这一行计算了新的容量,并将其赋值给 newCapacity 变量。新的容量是当前容量的两倍。

(3) 创建一个新的数组 newData,用于存储新扩容后的元素。新数组的大小为新的容量。

(4) 这是一个循环,用于将当前栈中的元素从旧数组 data 复制到新数组 newData 中。

(5) 释放了旧数组 data 所占用的内存空间。

(6) 将 newData 数组赋值给 data,使其成为栈的新存储数组。

(7) 更新了栈的容量为新的容量。

三、栈的销毁

template<typename T>

Stack<T>::~Stack() { // (1)

delete[] data; // (2)

}

(1) 这是析构函数的声明,用于在对象销毁时执行清理操作。

(2) 释放了动态分配的数组 data 所占用的内存空间。delete[] 用于释放动态分配的数组内存。

四、入栈

template<typename T>

void Stack<T>::push(T element) {

if (size == capacity) { // (1)

resize();

}

data[size++] = element; // (2)

}

(1) 如果栈的当前大小 size 等于容量 capacity,则调用 resize 函数进行扩容操作。代表如果栈已满,需要先进行扩容以增加栈的容量。

(2) 将元素 element 赋值给栈数组 data 的 size 位置,并将 size 的值增加 1,以表示栈中元素的数量增加。

五、出栈

template<typename T>

T Stack<T>::pop() {

if (size == 0) {

throw std::underflow_error("Stack is empty"); // (1)

}

return data[--size]; // (2)

}

(1) 检查栈是否为空。如果栈为空(即 size 为 0),则抛出一个 std::underflow_error 异常,提示 "Stack is empty"。

(2) 如果栈不为空,通过 --size 减小栈的大小,并返回 data 数组中栈顶元素的值。

六、获取栈顶元素

template<typename T>

T Stack<T>::top() const {

if (size == 0) {

throw std::underflow_error("Stack is empty");

}

return data[size - 1]; // (1)

}

(1) 如果栈不为空,返回 data 数组中最后一个元素的值,即栈顶元素。

七、栈的完整源码

#include <iostream>
#include <stdexcept>

using namespace std;

template<typename T>
class Stack {
private:
	T* data;
	int size;
	int capacity;
	void resize();
public:
	Stack() : data(new T[10]), size(0), capacity(10) {}
	~Stack();
	void push(T element);
	T pop();
	T top() const;
	int getSize() const;
};



template<typename T>
void Stack<T>::resize() {
	int newCapacity = capacity * 2;
	T* newData = new T[newCapacity];
	for (int i = 0; i < size; i++) {
		newData[i] = data[i];
	}
	delete[] data;
	data = newData;
	capacity = newCapacity;
}



template<typename T>

Stack<T>::~Stack() {
	delete[] data;
}



template<typename T>

void Stack<T>::push(T element) {
	if (size == capacity) {
		resize();
	}
	data[size++] = element;
}



template<typename T>

T Stack<T>::pop() {
	if (size == 0) {
		throw std::underflow_error("Stack is empty");
	}
	return data[--size];
}



template<typename T>
T Stack<T>::top() const {
	if (size == 0) {
		throw std::underflow_error("Stack is empty");
	}
	return data[size - 1];
}



template<typename T>
int Stack<T>::getSize() const {
	return size;
}





int main() {
	Stack<int> st;
	st.push(4);
	st.push(7);
	st.push(13);
	cout << st.top() << endl;
	st.push(17);
	cout << st.top() << endl;
	st.pop();
	st.pop();
	cout << st.top() << endl;
	cout << st.getSize() << endl;

	return 0;

}

C++链表实现

一、栈类的声明

栈是一种特殊的线性表,可以由顺序表来实现,也可以由链表来实现,这节课,我们采用链表来实现栈。

#include <iostream>
#include <stdexcept>

using namespace std;
template<typename T> // (1)

class Stack {
private:
	struct Node { // (2)
		T data;
		Node* next;
		Node(T d) : data(d), next(NULL) {}
	};
	Node* head; // (3)
	int size; // (4)
public:
	Stack() : head(NULL), size(0) {} // (5)
	~Stack(); // (6)
	void push(T element); // (7)
	T pop(); // (8)
	T top() const; // (9)
	int getSize() const; // (10)

};

(1) 这是一个模板声明,表示这个类是一个通用的类模板,可以用于处理各种不同类型的元素。

(2)这是一个结构体定义,用于表示栈中的节点。每个节点包含一个数据成员 data 和一个指向下一个节点的指针 next。

(3) head 是一个私有成员变量,用于保存栈的头节点指针。

(4) size 是一个私有成员变量,用于保存栈的大小。

(5) Stack() 是构造函数,用于初始化栈。它将头节点指针设置为 NULL,并将栈的大小设置为 0。

(6) ~Stack() 是析构函数,用于释放栈中分配的内存。

(7) void push(T element) 是一个公共成员函数,用于将一个元素压入栈顶。它创建一个新的节点,并将元素赋值给节点的数据成员,然后将新节点插入到栈的头部。

(8) T pop() 是一个公共成员函数,用于从栈顶弹出一个元素。它检查栈是否为空,如果不为空,则删除栈顶节点,并返回节点的数据成员。

(9) T top() const 是一个公共成员函数,用于获取栈顶元素,但不弹出它。它检查栈是否为空,如果不为空,则返回栈顶节点的数据成员。

(10) int getSize() const 是一个公共成员函数,用于获取栈的大小。

二、栈的扩容

由链表实现栈时,每次如果是新生成的结点,则不涉及到像顺序表那样的扩容操作。

三、栈的销毁

template<typename T>

Stack<T>::~Stack() { // (1)
	while (head != NULL) { // (2)
		Node* temp = head;
		head = head->next;
		delete temp;
	}
}

(1) 这是 Stack 类的析构函数的实现代码。它的作用是在对象销毁时,释放动态分配的节点内存。

(2) 不断循环访问栈中的元素,每次取出栈顶元素,存储到临时变量 temp 中,并且弹出栈顶,并且利用 delete 将弹出的元素进行内存释放,直到栈为空为止。

四、入栈

template<typename T>
void Stack<T>::push(T element) {
	Node* newNode = new Node(element); // (1)
	newNode->next = head; // (2)
	head = newNode; // (3)
	++size; // (4)
}

(1) 创建了一个新的 Node 对象,并将传入的元素赋值给该对象的数据成员。通过使用 new 操作符动态分配了内存来存储新的节点。

(2) 将新节点的 next 指针指向当前的头节点。这样,新节点就被添加到了栈的头部。

(3) 将头节点的指针更新为新节点,使新节点成为栈的新头部。

(4) 将栈的大小计数器加 1,以反映栈中元素数量的增加。

五、出栈

template<typename T>

T Stack<T>::pop() {
	if (head == NULL) {
		throw std::underflow_error("Stack is empty"); // (1)
	}
	T result = head->data; // (2)
	Node* temp = head; // (3)
	head = head->next; // (4)
	delete temp; // (5)
	--size; // (6)
	return result; // (7)
}

(1) 如果头节点为空(即栈为空),则抛出一个 std::underflow_error 异常,提示 "Stack is empty"。

(2) 将头节点的数据成员赋值给 result 变量,准备返回弹出的元素。

(3) 将头节点的指针赋值给 temp 变量,用于后续删除头节点。

(4) 将头节点的 next 指针赋值给头节点本身,从而将头节点从链表中移除。

(5) 调用 delete 操作符释放 temp 所指向的节点内存。

(6) 将栈的大小计数器减 1,以反映弹出操作后栈中元素数量的减少。

(7) 返回弹出的元素,通过 result 变量传递返回值。

六、获取栈顶元素

template<typename T>

T Stack<T>::top() const {
    if (head == NULL) {
        throw std::underflow_error("Stack is empty"); // (1)
    }
    return head->data; // (2)
}

(1) 如果栈为空,那么获取栈顶的这个操作是不合法的,抛出异常。

(2) 如果栈不为空,head 指针的 data 域,即栈顶元素。

七、栈的完整源码

#include <iostream>
#include <stdexcept>

using namespace std;

template<typename T>

class Stack {
private:
	struct Node {
		T data;
		Node* next;
		Node(T d) : data(d), next(NULL) {}
	};
	Node* head;
	int size;
public:
	Stack() : head(NULL), size(0) {}
	~Stack();
	void push(T element);
	T pop();
	T top() const;
	int getSize() const;
};



template<typename T>

Stack<T>::~Stack() {

	while (head != NULL) {
		Node* temp = head;
		head = head->next;
		delete temp;
	}
}

template<typename T>

void Stack<T>::push(T element) {
	Node* newNode = new Node(element);
	newNode->next = head;
	head = newNode;
	++size;
}

template<typename T>

T Stack<T>::pop() {
	if (head == NULL) {
		throw std::underflow_error("Stack is empty");
	}
	T result = head->data;
	Node* temp = head;
	head = head->next;
	delete temp;
	--size;
	return result;
}



template<typename T>

T Stack<T>::top() const {
	if (head == NULL) {
		throw std::underflow_error("Stack is empty");
	}
	return head->data;
}



template<typename T>

int Stack<T>::getSize() const {
	return size;
}


int main() {

	Stack<int> st;
	st.push(4);
	st.push(7);
	st.push(13);
	cout << st.top() << endl;
	st.push(17);
	cout << st.top() << endl;
	st.pop();
	st.pop();
	cout << st.top() << endl;
	cout << st.getSize() << endl;
	return 0;

}

题集

1. 进制转换

2. Bitset

#include <iostream>
#include <bitset>
using namespace std;

int main() {
    int n;
    // 循环读取输入直到文件结束
    while (cin >> n) {
        // 使用 bitset 将整数转换为二进制字符串
        // 因为 0 < n < 1000, 所以我们最多需要 10 位来表示 n 的二进制形式 (2^10 = 1024 > 1000)
        cout << bitset<10>(n).to_string().substr(10 - (int)log2(n) - 1) << endl;
    }
    return 0;
}

这段代码中,我们使用了bitset库,它可以方便地将整数转换为固定长度的二进制字符串。由于题目中指出0 < n < 1000,我们知道最大值999的二进制表示是1111100111,这需要10个二进制位来表示。所以我们创建了一个大小为10的bitset。

然而,当使用bitset<10>(n).to_string()时,它总是返回一个长度为10的字符串,即使对于较小的数字也是如此,比如1会被表示为0000000001。为了去除前导零,我们计算了数字n在二进制下的实际位数(即log2(n)+1),然后从结果字符串中切片得到正确的二进制表示。

需要注意的是,对于n=1的情况,log2(n)会给出0,所以我们对log2(n)的结果进行了类型转换并加1,确保我们不会尝试访问无效的索引。对于n=0的情况,题目已经说明不会出现,因此无需特别处理。

另外,如果想要更简单的实现,可以忽略bitset和log2,而使用循环方式构建二进制字符串,或者使用标准库中的std::stoi与std::string结合自定义逻辑去除前导零。

3. 图书整理 I

4. 回文链表

5. 括号的最大嵌套深度

6. 有效的括号

普通栈

剑指 Offer 06. 从尾到头打印链表

反转链表

括号的最大嵌套深度

有效的括号

最长有效括号

剑指 Offer II 027. 回文链表

回文链表

回文链表

棒球比赛

剑指 Offer II 036. 后缀表达式

比较含退格的字符

三合一

验证栈序列

剑指 Offer 31. 栈的压入、弹出序列

从先序遍历还原二叉树

单调栈

最小栈

栈的最小值

剑指 Offer 30. 包含min函数的栈

剑指 Offer II 038. 每日温度

用栈实现队列

剑指 Offer 09. 用两个栈实现队列

化栈为队

最后 K 个数的乘积

去除重复字母

不同字符的最小子序列

柱状图中最大的矩形

剑指 Offer II 039. 直方图最大矩形面积

最大矩形

剑指 Offer II 040. 矩阵中最大的矩形

接雨水

直方图的水量

最大子矩阵

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

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

相关文章

博弈论3:图游戏SG函数(Graph Games)

目录 一、图游戏是什么 1.游戏特征 2.游戏实例 二、图游戏的必胜策略 1.SG 函数&#xff08;Sprague-Grundy Function&#xff09; 2.必胜策略&#xff08;利用SG函数&#xff09; 3.拿走游戏转化成图游戏&#xff08;Take-away Game -> Graph Game&#xff09; 一、图…

0101多级nginx代理websocket配置-nginx-web服务器

1. 前言 项目一些信息需要通过站内信主动推动给用户&#xff0c;使用websocket。web服务器选用nginx&#xff0c;但是域名是以前通过阿里云申请的&#xff0c;解析ip也是阿里云的服务器&#xff0c;甲方不希望更换域名。新的系统需要部署在内网服务器&#xff0c;简单拓扑图如…

qt-C++笔记之自定义类继承自 `QObject` 与 `QWidget` 及开发方式详解

qt-C笔记之自定义类继承自 QObject 与 QWidget 及开发方式详解 code review! 参考笔记 1.qt-C笔记之父类窗口、父类控件、对象树的关系 2.qt-C笔记之继承自 QWidget和继承自QObject 并通过 getWidget() 显示窗口或控件时的区别和原理 3.qt-C笔记之自定义类继承自 QObject 与 QW…

Elastic 8.17:Elasticsearch logsdb 索引模式、Elastic Rerank 等

作者&#xff1a;来自 Elastic Brian Bergholm 今天&#xff0c;我们很高兴地宣布 Elastic 8.17 正式发布&#xff01; 紧随一个月前发布的 Elastic 8.16 之后&#xff0c;我们将 Elastic 8.17 的重点放在快速跟踪关键功能上&#xff0c;这些功能将带来存储节省和搜索性能优势…

[C++]类的继承

一、什么是继承 1.定义&#xff1a; 在 C 中&#xff0c;继承是一种机制&#xff0c;允许一个类&#xff08;派生类&#xff09;继承另一个类&#xff08;基类&#xff09;的成员&#xff08;数据和函数&#xff09;。继承使得派生类能够直接访问基类的公有和保护成员&#xf…

Docker 用法详解

文章目录 一、Docker 快速入门1.1 部署 MYSQL1.2 命令解读&#xff1a; 二、Docker 基础2.1 常见命令&#xff1a;2.1.1 命令介绍&#xff1a;2.1.2 演示&#xff1a;2.1.3 命令别名&#xff1a; 2.2 数据卷&#xff1a;2.2.1 数据卷简介&#xff1a;2.2.2 数据卷命令&#xff…

【自动化】Python SeleniumUtil 油猴 工具 自动安装用户脚本

【自动化】Python SeleniumUtil 油猴 工具 【自动化】Python SeleniumUtil 工具-CSDN博客【自动化】Python SeleniumUtil 工具。https://blog.csdn.net/G971005287W/article/details/144565691 油猴工具 import timefrom selenium.webdriver.support.wait import WebDriverW…

盛元广通畜牧与水产品检验技术研究所LIMS系统

一、系统概述 盛元广通畜牧与水产品检验技术研究所LIMS系统集成了检测流程管理、样品管理、仪器设备管理、质量控制、数据记录与分析、合规性管理等功能于一体&#xff0c;能够帮助实验室实现全流程的数字化管理。在水产、畜牧产品的质检实验室中&#xff0c;LIMS系统通过引入…

clickhouse-数据库引擎

1、数据库引擎和表引擎 数据库引擎默认是Ordinary&#xff0c;在这种数据库下面的表可以是任意类型引擎。 生产环境中常用的表引擎是MergeTree系列&#xff0c;也是官方主推的引擎。 MergeTree是基础引擎&#xff0c;有主键索引、数据分区、数据副本、数据采样、删除和修改等功…

GEE+本地XGboot分类

GEE本地XGboot分类 我想做提取耕地提取&#xff0c;想到了一篇董金玮老师的一篇论文&#xff0c;这个论文是先提取的耕地&#xff0c;再做作物分类&#xff0c;耕地的提取代码是开源的。 但这个代码直接在云端上进行分类&#xff0c;GEE会爆内存&#xff0c;因此我准备把数据下…

Docker搭建kafka环境

系统&#xff1a;MacOS Sonoma 14.1 Docker版本&#xff1a;Docker version 27.3.1, build ce12230 Docker desktop版本&#xff1a;Docker Desktop 4.36.0 (175267) 1.拉取镜像 先打开Docker Desktop&#xff0c;然后在终端执行命令 docker pull lensesio/fast-data-dev …

校园点餐订餐外卖跑腿Java源码

简介&#xff1a; 一个非常实用的校园外卖系统&#xff0c;基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化&#xff0c;提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合&am…

Linux文件属性 --- 硬链接、所有者、所属组

三、硬链接数 1.目录 使用“ll”命令查看&#xff0c;在文件权限的后面有一列数字&#xff0c;这是文件的硬链接数。 对于目录&#xff0c;硬链接的数量是它具有的直接子目录的数量加上其父目录和自身。 下图的“qwe”目录就是“abc”目录的直接子目录。 2.文件 对于文件可…

Centos7 部署ZLMediakit

1、拉取代码 #国内用户推荐从同步镜像网站gitee下载 git clone --depth 1 https://gitee.com/xia-chu/ZLMediaKit cd ZLMediaKit #千万不要忘记执行这句命令 git submodule update --init 2、安装编译器 sudo yum -y install gcc 3、安装cmake sudo yum -y install cmake 4…

无管理员权限 LCU auth-token、port 获取(全网首发 go)

一&#xff1a; 提要&#xff1a; 参考项目&#xff1a; https://github.com/Zzaphkiel/Seraphine 想做一个 lol 查战绩的软件&#xff0c;并且满足自己的需求&#xff08;把混子和大爹都表示出来&#xff09;&#xff0c;做的第一步就是获取 lcu token &#xff0c;网上清一色…

《云原生安全攻防》-- K8s安全框架:认证、鉴权与准入控制

从本节课程开始&#xff0c;我们将来介绍K8s安全框架&#xff0c;这是保障K8s集群安全比较关键的安全机制。接下来&#xff0c;让我们一起来探索K8s安全框架的运行机制。 在这个课程中&#xff0c;我们将学习以下内容&#xff1a; K8s安全框架&#xff1a;由认证、鉴权和准入控…

spring\strust\springboot\isp前后端那些事儿

后端 一. 插入\更新一条数据&#xff08;老&#xff09; Map<String, Object> parameterMap MybatisUtil.initParameterSave("Send_ProjectFrozenLog", sendProjectFrozenLog); commonMapper.insert(parameterMap);parameterMap MybatisUtil.initParameter…

UE5安装Fab插件

今天才知道原来Fab也有类似Quixel Bridge的插件&#xff0c;于是立马就安装上了&#xff0c;这里分享一下安装方法 在Epic客户端 - 库 - Fab Library 搜索 Fab 即可安装Fab插件 然后重启引擎&#xff0c;在插件面板勾选即可 然后在窗口这就有了 引擎左下角也会多出一个Fab图标…

对于使用exe4j打包,出现“NoClassDefFoundError: BOOT-INF/classes”的解决方案

jar使用exe4j打包exe&#xff0c;出现NoClassDefFoundError: BOOT-INF/classes 注意选取的jar包是使用build&#xff0c;而不是maven中的install 本文介绍解决这个方法的方案 点击Project Structure 按照如图所示选择 选择main class&#xff0c;选择你要打的main 如果遇到/M…

文件上传之文件内容检测

一.基本概念 介绍&#xff1a;文件内容检测就是检测上传的文件里的内容。 文件幻数检测 通常情况下&#xff0c;通过判断前10个字节&#xff0c;基本就能判断出一个文件的真实类型。 文件加载检测 一般是调用API或函数对文件进行加载测试。常见的是图像渲染测试&#xff0c;再…