数据结构-栈容器的实现

栈的实现

栈是一种重要的数据结构,是一种线性结构,具有后进先出的特点.
因为线性表有 2 种实现方式,故栈也有 2 种实现方式,即顺序存储结构和链式存储结构.
本篇将以链式存储结构给出一种实现方式.

链栈的存储结构

和单链表的存储结构类似,链栈的存储映像也包括数据域指针域两个部分.存储结构表示如下:

class StackNode
{
// 这里不妨先用 T 来表示数据类型,后面将看到它的作用
T data_;
StackNode* next_;
};

为了方便管理一组栈节点不妨定义个 Stack 类如下:

class Stack
{
private:
StackNode<T>* ptr_;
// 这里的 T 表示它所指向的栈节点数据域是 T 类型的
};

栈的主要操作

1. 初始化

初始化的任务就是构造一个空栈,与链表不同的是,链栈没有必要设置头节点,故只需将栈顶指针制空即可.可利用 Stack 类构造函数实现,如下:

Stack()
{
ptr_ = nullptr;
}

2. 入栈

算法步骤

  1. 创建一个 StackNode 对象,用指针 ptr 指向.
  2. 将节点数据域制为 data.
  3. 将新节点插入栈顶.
  4. 修改栈顶指针.

具体实现如下:

bool Stack::push(T data)
{
StackNode* ptr = new StackNode(data, ptr_);
ptr_ = ptr;
return true;
}

3. 出栈

算法步骤

  1. 判断栈是否为空,若空返回 false.
  2. 将栈顶元素赋给 data.
  3. 临时保存栈顶元素的空间,以备释放
  4. 修改栈顶指针,指向新的栈顶元素.
  5. 释放原栈顶元素的指针.

具体实现如下:

bool Stack::pop(T& data)
{
if (ptr_ == nullptr)
{
return false;
}
data = ptr_->data_;
StackNode* ptr = ptr_;
ptr_ = ptr_->next_;
delete ptr;
return true;
}

其中 get_dataget_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;
}

结果如下:
测试结果2
这就很爽了,利用泛型使得我们的栈不仅可以存储基本数据类型,还能存储用户自定义类型。嗯,这可能就叫一次编程,终身受益吧!

posted @   markgo  阅读(74)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话
点击右上角即可分享
微信分享提示