s4栈学习和链栈的实现
一、核心知识点回顾
栈(Stack)的特性栈是一种后进先出(LIFO, Last In First Out) 的线性数据结构,仅允许在一端(栈顶)进行插入(
push
)和删除(pop
)操作,常用操作包括:push
:在栈顶插入元素pop
:移除栈顶元素getTop
:获取栈顶元素(不删除)isEmpty
:判断栈是否为空getSize
:获取栈中元素数量
链表实现栈的优势栈的实现通常有两种方式:数组和链表。本代码采用链表实现,优势在于:
- 动态扩容:无需预先指定大小,元素数量可灵活增长
- 内存高效:仅占用实际存储元素所需的内存,无数组的 “预分配冗余”
- 操作便捷:栈顶插入 / 删除元素的时间复杂度为
O(1)
模板类(Template)代码使用
template <class T>
定义模板类,使ListStack
能存储任意数据类型(如int
、string
等),实现了代码的复用性和泛型编程思想。
二、代码逐部分讲解
1. 栈节点结构(StackNode
)
template <class T>
struct StackNode {T value; // 存储节点的值StackNode *next; // 指向后继节点(栈底方向的节点)// 构造函数:初始化值和指针(默认值为T的默认构造,指针为nullptr)StackNode(const T &val = T(), StackNode*n = nullptr) : value(val), next(n) {}
};
- 每个节点包含两部分:
value
(存储数据)和next
(指向栈的下一个元素,即更靠近栈底的元素) - 构造函数支持自定义初始化,简化新节点的创建
2. 栈类(ListStack
)的核心成员
template <typename T>
class ListStack {int size; // 栈中元素的数量StackNode<T> *top; // 指向栈顶节点的指针(栈的入口)
public:// 构造函数:初始化栈为空(size=0,top=nullptr)ListStack() : size(0), top(nullptr) {}// ... 成员函数 ...
};
size
:记录元素数量,避免每次需要遍历链表计数(O(1)
获取大小)top
:栈顶指针,所有插入 / 删除操作都围绕top
进行
3. 核心操作实现
(1)入栈(push
)
void push(const T& val) {// 创建新节点,值为val,next指向当前栈顶(新节点成为新的栈顶)StackNode<T>* newNode = new StackNode<T>(val, top);top = newNode; // 更新栈顶指针为新节点size++; // 元素数量+1
}
- 流程:创建新节点 → 新节点的
next
指向原栈顶 → 更新top
为新节点 → 增大size
- 时间复杂度:
O(1)
(仅需修改指针)
(2)出栈(pop
)
void pop() {if (isEmpty()) {throw std::runtime_error("栈为空,无法执行出栈操作");}StackNode<T>* temp = top; // 保存当前栈顶节点top = top->next; // 栈顶指针后移(指向原栈顶的下一个节点)delete temp; // 释放原栈顶节点的内存(避免内存泄漏)size--; // 元素数量-1
}
- 注意:出栈前必须检查栈是否为空(否则会访问
nullptr
,导致程序崩溃) - 异常处理:栈空时抛出异常,避免错误操作
- 时间复杂度:
O(1)
(3)获取栈顶元素(getTop
)
T& getTop() {if (isEmpty()) {throw std::runtime_error("栈为空,无法获取栈顶元素");}return top->value; // 返回栈顶节点的值(引用类型,支持修改)
}
- 同样需要检查栈是否为空,避免访问空指针
- 返回引用(
T&
):允许直接修改栈顶元素的值(如intStack.getTop() = 100;
)
(4)其他辅助操作
isEmpty()
:通过size == 0
判断栈是否为空(O(1)
)getSize()
:直接返回size
(O(1)
)clear()
:通过循环调用pop()
清空所有元素(O(n)
,n
为元素数量)print()
:从栈顶到栈底遍历链表,打印所有元素(O(n)
)
4. 主函数(main
)测试
int main() {// 测试int类型的栈ListStack<int> intStack;intStack.push(10);intStack.push(20);intStack.push(30);intStack.print(); // 栈顶->栈底:30 20 10(符合LIFO特性)// 测试string类型的栈ListStack<std::string> strStack;strStack.push("Hello");strStack.push("World");strStack.print(); // 栈顶->栈底:World Helloreturn 0;
}
- 验证了模板类的泛型能力:同一
ListStack
类可处理int
、string
等不同类型 - 输出结果符合栈的 “后进先出” 特性:最后入栈的元素在最前面打印
三、总结
设计亮点
- 采用链表实现,动态性好,无固定大小限制
- 模板类设计实现泛型编程,支持多数据类型
- 操作安全性高:空栈操作时抛出异常,避免崩溃
- 内存管理完善:
pop()
和clear()
中显式释放内存,防止泄漏
可优化点
- 可添加拷贝构造函数和赋值运算符重载,避免浅拷贝问题(默认的拷贝会导致两个栈共用同一份链表,释放时双重删除)
- 可将
print()
改为迭代器遍历,更符合 STL 风格 - 异常处理可改为返回
bool
值(根据场景选择异常或返回值方式)
这段代码完整实现了栈的核心功能,是链表和栈数据结构结合的典型案例,同时展示了模板编程的灵活性,适合作为数据结构入门的实践代码。
完整代码:
#include <iostream>
using namespace std;
template <class T>
struct StackNode
{T value;StackNode *next;StackNode(const T &val = T(), StackNode*n = nullptr) : value(val), next(n) {}
};
template <typename T>
class ListStack
{int size;StackNode<T> *top;public:ListStack() : size(0), top(nullptr) {}void push(const T& val)//在栈顶插入新元素{StackNode<T>* newNode = new StackNode<T>(val);newNode->next=top;top=newNode;size++;}void pop()//移除栈顶元素{if (isEmpty()) {throw std::runtime_error("栈为空,无法执行出栈操作");}// 保存当前栈顶节点StackNode<T>* temp = top;// 更新栈顶指针top = top->next;// 释放原栈顶节点的内存delete temp;// 栈大小减1size--;}T& getTop()//获取栈顶元素{if (isEmpty()) {throw std::runtime_error("栈为空,无法获取栈顶元素");}return top->value;}bool isEmpty()//判断栈是否为空{return size==0;}int getSize()//获取栈中元素个数{return size;}void clear()//清空栈中所有元素{while(!isEmpty()){pop();}}void print()//打印栈中所有元素{if (isEmpty()) {std::cout << "栈为空" << std::endl;return;}std::cout << "栈元素(栈顶->栈底):";StackNode<T>* current = top;while (current != nullptr) {std::cout << current->value<< " ";current = current->next;}std::cout << std::endl;}
};
int main(){ListStack<int> intStack;// 入栈操作intStack.push(10);intStack.push(20);intStack.push(30);intStack.print(); // 输出:栈元素(栈顶->栈底):30 20 10// 获取栈顶元素std::cout << "栈顶元素:" << intStack.getTop() << std::endl; // 输出:30// 出栈操作intStack.pop();intStack.print(); // 输出:栈元素(栈顶->栈底):20 10// 查看栈大小std::cout << "栈大小:" << intStack.getSize() << std::endl; // 输出:2// 创建一个存储字符串的链栈ListStack<std::string> strStack;strStack.push("Hello");strStack.push("World");strStack.print(); // 输出:栈元素(栈顶->栈底):World Helloreturn 0;
}