29.参考c/c++堆栈实现自己的堆栈。要求:不能用stl容器。

2026-01-23
1985 字 · 10 分钟

🔬 自定义堆栈实现(不依赖STL容器)

📖 内容概览

本文将详细介绍如何在C++中实现自定义堆栈数据结构,包括基于数组的实现方案、核心功能实现和使用方法,且不依赖STL容器。通过手动实现堆栈,可以深入理解数据结构的设计原理和C++的内存管理机制。

🎯 核心概念

🧩 堆栈的定义与特性

堆栈(Stack)是一种**后进先出(LIFO, Last In First Out)**的线性数据结构,其操作受限:

  • 只能在表的一端(称为栈顶)进行插入和删除操作
  • 插入操作称为入栈(Push)
  • 删除操作称为出栈(Pop)

🔧 堆栈的设计思路

使用数组实现堆栈的核心设计要点:

  1. 动态内存管理:使用指针和动态数组存储元素
  2. 栈顶指针:使用整数索引标记栈顶位置
  3. 边界检查:入栈前检查是否已满,出栈前检查是否为空
  4. 异常处理:对非法操作进行适当的错误提示
  5. 资源管理:确保析构函数正确释放动态分配的内存

📋 堆栈应实现的核心功能

功能描述
入栈(Push)将元素添加到栈顶
出栈(Pop)移除并返回栈顶元素
取栈顶(Top)获取栈顶元素但不移除
判空(IsEmpty)检查堆栈是否为空
判满(IsFull)检查堆栈是否已满
栈长度(Size)获取堆栈中元素个数
清空(Clear)清空堆栈中的所有元素
销毁(Destroy)释放堆栈占用的资源

🛠️ 代码实现

📝 基于数组的堆栈实现

#include <iostream>
using namespace std;
// 基于数组实现的堆栈类
template <typename T> // 使用模板支持任意类型
class Stack {
public:
// 构造函数:创建指定容量的堆栈
explicit Stack(size_t capacity = 10) {
this->capacity_ = capacity;
data_ = new T[capacity_]; // 动态分配数组
top_ = -1; // 栈顶指针初始化为-1,表示空栈
}
// 析构函数:释放动态分配的内存
~Stack() {
delete[] data_;
data_ = nullptr;
capacity_ = 0;
top_ = -1;
}
// 入栈操作
bool Push(const T& value) {
if (IsFull()) {
cout << "栈已满,无法入栈!" << endl;
return false;
}
data_[++top_] = value; // 先递增栈顶指针,再存储元素
return true;
}
// 出栈操作
bool Pop() {
if (IsEmpty()) {
cout << "栈为空,无法出栈!" << endl;
return false;
}
--top_; // 仅需递减栈顶指针
return true;
}
// 获取栈顶元素(不修改栈)
T& Top() {
if (IsEmpty()) {
throw runtime_error("栈为空,无法获取栈顶元素!");
}
return data_[top_];
}
// 获取栈顶元素(const版本)
const T& Top() const {
if (IsEmpty()) {
throw runtime_error("栈为空,无法获取栈顶元素!");
}
return data_[top_];
}
// 检查栈是否为空
bool IsEmpty() const {
return top_ == -1;
}
// 检查栈是否已满
bool IsFull() const {
return top_ == static_cast<int>(capacity_) - 1;
}
// 获取栈的当前大小
size_t Size() const {
return top_ + 1;
}
// 获取栈的容量
size_t Capacity() const {
return capacity_;
}
// 清空栈
void Clear() {
top_ = -1; // 直接将栈顶指针重置为-1
}
// 打印栈中元素
void Print() const {
if (IsEmpty()) {
cout << "栈为空!" << endl;
return;
}
cout << "栈内元素(从栈顶到栈底):";
for (int i = top_; i >= 0; --i) {
cout << data_[i] << " ";
}
cout << endl;
}
private:
T* data_; // 存储元素的动态数组
size_t capacity_; // 栈的容量
int top_; // 栈顶指针,-1表示空栈
// 禁止拷贝构造和赋值操作(可选)
Stack(const Stack&) = delete;
Stack& operator=(const Stack&) = delete;
};
// 测试函数
void TestStack() {
Stack<int> stack(5); // 创建容量为5的整型堆栈
// 测试入栈
cout << "=== 测试入栈操作 ===" << endl;
stack.Push(10);
stack.Push(20);
stack.Push(30);
stack.Print();
// 测试栈顶元素
cout << "\n=== 测试栈顶元素 ===" << endl;
cout << "当前栈顶元素:" << stack.Top() << endl;
// 测试栈大小
cout << "\n=== 测试栈大小 ===" << endl;
cout << "当前栈大小:" << stack.Size() << endl;
cout << "栈容量:" << stack.Capacity() << endl;
// 测试出栈
cout << "\n=== 测试出栈操作 ===" << endl;
stack.Pop();
cout << "出栈后栈大小:" << stack.Size() << endl;
// 测试判空和判满
cout << "\n=== 测试判空和判满 ===" << endl;
cout << "栈是否为空:" << (stack.IsEmpty() ? "是" : "否") << endl;
// 测试栈满情况
cout << "\n=== 测试栈满情况 ===" << endl;
stack.Push(40);
stack.Push(50);
stack.Push(60); // 超出容量,应该失败
cout << "栈是否已满:" << (stack.IsFull() ? "是" : "否") << endl;
// 测试清空栈
cout << "\n=== 测试清空栈 ===" << endl;
stack.Clear();
cout << "清空后栈大小:" << stack.Size() << endl;
}
int main() {
TestStack();
return 0;
}

代码优化说明

  1. 使用模板:支持任意数据类型,提高复用性
  2. 动态内存管理:构造函数分配内存,析构函数释放内存
  3. 栈顶指针设计:使用-1表示空栈,更直观
  4. 异常处理:对非法操作(如访问空栈顶)抛出异常
  5. 禁止拷贝:避免浅拷贝问题,可根据需要开放
  6. 清晰的接口:函数命名规范,职责明确
  7. 完整的测试:涵盖各种边界情况

🛠️ 基于链表的堆栈实现(扩展)

除了基于数组的实现,还可以使用链表实现堆栈,具有动态扩容的优势:

#include <iostream>
using namespace std;
// 链表节点定义
template <typename T>
struct Node {
T data;
Node* next;
Node(const T& value) : data(value), next(nullptr) {}
};
// 基于链表实现的堆栈类
template <typename T>
class LinkedListStack {
public:
LinkedListStack() : head_(nullptr), size_(0) {}
~LinkedListStack() {
Clear();
}
// 入栈操作
void Push(const T& value) {
Node<T>* new_node = new Node<T>(value);
new_node->next = head_;
head_ = new_node;
++size_;
}
// 出栈操作
bool Pop() {
if (IsEmpty()) {
cout << "栈为空,无法出栈!" << endl;
return false;
}
Node<T>* temp = head_;
head_ = head_->next;
delete temp;
--size_;
return true;
}
// 获取栈顶元素
T& Top() {
if (IsEmpty()) {
throw runtime_error("栈为空,无法获取栈顶元素!");
}
return head_->data;
}
bool IsEmpty() const {
return head_ == nullptr;
}
size_t Size() const {
return size_;
}
void Clear() {
while (head_ != nullptr) {
Node<T>* temp = head_;
head_ = head_->next;
delete temp;
}
size_ = 0;
}
void Print() const {
if (IsEmpty()) {
cout << "栈为空!" << endl;
return;
}
cout << "栈内元素(从栈顶到栈底):";
Node<T>* current = head_;
while (current != nullptr) {
cout << current->data << " ";
current = current->next;
}
cout << endl;
}
private:
Node<T>* head_; // 链表头指针,指向栈顶元素
size_t size_; // 栈的大小
};

🛠️ 两种实现方式的对比

实现方式优点缺点
基于数组内存连续,访问效率高固定容量,扩容需要重新分配内存
基于链表动态扩容,不需要预先指定容量内存不连续,访问效率较低,每个节点有额外开销

📋 总结与最佳实践

  1. 选择合适的实现方式
    • 对于固定大小或可预测大小的场景,选择基于数组的实现
    • 对于大小变化较大的场景,选择基于链表的实现
  2. 内存管理注意事项
    • 确保动态分配的内存被正确释放
    • 考虑是否需要支持拷贝构造和赋值操作
    • 对于模板类,注意编译时的模板实例化
  3. 接口设计原则
    • 保持接口简洁明了
    • 提供必要的错误检查和异常处理
    • 考虑接口的易用性和安全性
  4. 测试建议
    • 测试各种边界情况(空栈、满栈)
    • 测试正常的入栈出栈操作
    • 测试异常情况的处理
    • 考虑多线程环境下的安全性(如果需要)
  5. 性能优化
    • 对于数组实现,可以考虑实现自动扩容机制
    • 对于链表实现,可以考虑使用内存池优化节点分配 通过手动实现堆栈,可以深入理解数据结构的设计原理和C++的内存管理机制,这对于掌握高级C++编程非常有帮助。无论是基于数组还是基于链表的实现,都需要关注资源管理和异常处理,确保代码的健壮性和安全性。

Thanks for reading!

29.参考c/c++堆栈实现自己的堆栈。要求:不能用stl容器。

2026-01-23
1985 字 · 10 分钟

已复制链接

评论区

目录