数据结构-栈容器的实现
栈的实现
栈是一种重要的数据结构,是一种线性结构,具有后进先出的特点.
因为线性表有 2 种实现方式,故栈也有 2 种实现方式,即顺序存储结构和链式存储结构.
本篇将以链式存储结构给出一种实现方式.
链栈的存储结构
和单链表的存储结构类似,链栈的存储映像也包括数据域和指针域两个部分.存储结构表示如下:
class StackNode { // 这里不妨先用 T 来表示数据类型,后面将看到它的作用 T data_; StackNode* next_; };
为了方便管理一组栈节点不妨定义个 Stack 类如下:
class Stack { private: StackNode<T>* ptr_; // 这里的 T 表示它所指向的栈节点数据域是 T 类型的 };
栈的主要操作
1. 初始化
初始化的任务就是构造一个空栈,与链表不同的是,链栈没有必要设置头节点,故只需将栈顶指针制空即可.可利用 Stack 类构造函数实现,如下:
Stack() { ptr_ = nullptr; }
2. 入栈
算法步骤
- 创建一个 StackNode 对象,用指针 ptr 指向.
- 将节点数据域制为 data.
- 将新节点插入栈顶.
- 修改栈顶指针.
具体实现如下:
bool Stack::push(T data) { StackNode* ptr = new StackNode(data, ptr_); ptr_ = ptr; return true; }
3. 出栈
算法步骤
- 判断栈是否为空,若空返回
false
. - 将栈顶元素赋给 data.
- 临时保存栈顶元素的空间,以备释放
- 修改栈顶指针,指向新的栈顶元素.
- 释放原栈顶元素的指针.
具体实现如下:
bool Stack::pop(T& data) { if (ptr_ == nullptr) { return false; } data = ptr_->data_; StackNode* ptr = ptr_; ptr_ = ptr_->next_; delete ptr; return true; }
其中 get_data
和 get_next
分别为返回当前节点的数据元素和指针的函数.
4. 取栈顶元素
当栈非空时,返回当前栈顶元素的值,具体实现如下:
T Stack::get_top() { if (ptr_ != nullptr) return ptr_->data_; }
当然除了这些还可以定义其它接口,比如说,求栈长、清空栈等.这里就不一一赘述了.
其实到这里为止栈的数据结构基本已经实现完了,但任有改进的余地.
比方说,如果要同时使用存储 int
型数据的栈和 double
型数据的栈.上述代码就显得无能为力了. 你当然可以单独定义一个DoubleStack
类来存储 double
型数据.但为每种类型都单独定义相同功能的类的话是不现实的(因为还有用户自定义类型), 应用范围有限.那有没有一种方法可以定义一次对各种类型都适用呢? 有! C++ 的泛型.
栈容器的实现
其实在这里我们只要稍微修改我们的代码,就可以让我们的栈存储任意类型的数据了(这才叫容器嘛).基本思想一样,整合如下:
#pragma once template <typename T> // 用于创建一个栈 class Stack { public: template <typename T> // 用于生成栈节点 class StackNode { public: StackNode() { next_ = nullptr; } StackNode(T data) { data_ = data; next_ = nullptr; } StackNode(T data, StackNode* next) { data_ = data; next_ = next; } // 栈节点的数据域 T data_; // 指向下一个栈节点的指针域 StackNode* next_; }; // 在栈顶插入元素 bool push(T); // 弹出栈顶元素,并保存 bool pop(T&); // 弹出栈顶元素 bool pop(); // 取栈顶元素 T get_top(); // 清空栈 void clear(); // 求栈长 size_t length(); // 判断栈是否为空 bool is_empty(); Stack() { ptr_ = nullptr; length_ = 0; } private: // 栈的头指针,可标志一个栈 StackNode<T>* ptr_; // 栈的长度 size_t length_; }; template <typename T> bool Stack<T>::push(T data) { StackNode<T>* ptr = new StackNode<T>(data, ptr_); ptr_ = ptr; ++length_; return true; } template <typename T> bool Stack<T>::pop(T& data) { if (ptr_ == nullptr) { return false; } data = ptr_->data_; StackNode<T>* ptr = ptr_; ptr_ = ptr_->next_; delete ptr; --length_; return true; } template <typename T> bool Stack<T>::pop() { if (ptr_ == nullptr) { return false; } StackNode<T>* ptr = ptr_; ptr_ = ptr_->next_; delete ptr; --length_; return true; } template <typename T> inline T Stack<T>::get_top() { return ptr_->data_; } template <typename T> void Stack<T>::clear() { while (ptr_) { StackNode<T>* ptr = ptr_; ptr_ = ptr_->next_; delete ptr; } length_ = 0; } template <typename T> inline size_t Stack<T>::length() { return length_; } template <typename T> bool Stack<T>::is_empty() { if (ptr_) { return false; } return true; }
测试
可写出测试代码来测试代码是否正确,如下:
#include <iostream> #include "linkstack.h" // 这里存放我们编写的栈容器 using namespace std; int main(void) { Stack<double> double_stack; Stack<int> int_stack; for (size_t i = 1; i < 10; i++) { double_stack.push(i * 2.5); int_stack.push(i); } cout << "double 栈的长度:" << double_stack.length() << endl; cout << "int 栈的长度:" << int_stack.length() << endl; cout << "弹出 double 栈前5个元素\n"; for (size_t i = 1; i < 5; i++) { double data; double_stack.pop(data); cout << data << " "; } cout << "\ndouble 栈:我的长度变了。变成:" << double_stack.length() << endl; cout << "int 栈:我的长度没变。还是:" << int_stack.length() << endl; return 0; }
运行结果如下:
接下来测试是否可以正确存放用户自定义类型,可定义一个平面点集如下:
注:这里不要缺省默认构造函数
class CPoint { private: int x_; int y_; public: CPoint() { x_ = 0; y_ = 0; } CPoint(int x, int y) { x_ = x; y_ = y; } friend std::ostream& operator<<(std::ostream& out, CPoint point) { out << "(" << point.x_ << "," << point.y_ << ")"; return out; } };
测试代码如下:
#include <iostream> #include "linkstack.h" // 这里存放我们编写的栈容器 using namespace std; int main(void) { Stack<CPoint> stack_point; for (size_t i = 1; i < 10; i++) { CPoint point(i, i * i); stack_point.push(point); } cout << "现在栈的长度为:" << stack_point.length() << endl; cout << "弹出所有的元素:" << endl; while (!stack_point.is_empty()) { CPoint point; stack_point.pop(point); ::std::cout <<point << " "; } cout << "\n现在栈的长度为:" << stack_point.length() << endl; system("pause"); return 0; }
结果如下:
这就很爽了,利用泛型使得我们的栈不仅可以存储基本数据类型,还能存储用户自定义类型。嗯,这可能就叫一次编程,终身受益吧!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话