栈与队列的实现

简介: 栈与队列的实现

栈与队列的实现

使用数组或链表实现栈和队列,并讨论它们的应用场景和性能特点。

 

 

栈(Stack)

实现

数组实现:

栈顶指针(top):用于指向栈顶元素的位置。

初始化时,栈顶指针通常设置为-1(如果栈为空)或0(如果栈可以包含空元素)。

入栈(push):在栈顶添加元素,栈顶指针加1。

出栈(pop):移除栈顶元素,栈顶指针减1。

访问栈顶元素(peek/top):返回栈顶元素的值,但不移除它。

链表实现:

使用链表的头部作为栈顶。

入栈:在链表头部添加新节点。

出栈:移除链表头部节点。

访问栈顶元素:访问链表头部节点的值。

应用场景

后缀表达式求值

深度优先搜索(DFS)

浏览器的前进后退功能

撤销操作(如文本编辑器的撤销)

性能特点

数组实现:

时间复杂度:入栈、出栈、访问栈顶元素均为O(1)。

空间复杂度:O(n),n为栈中元素数量。

缺点:栈大小受限,可能需要动态扩容。

链表实现:

时间复杂度:入栈、出栈、访问栈顶元素均为O(1)(不考虑链表节点分配的时间)。

空间复杂度:O(n),n为栈中元素数量。

优点:动态大小,不需要预先分配空间。

队列(Queue)

实现

数组实现:

队首指针(front):指向队列第一个元素。

队尾指针(rear):指向队列最后一个元素的下一个位置(或最后一个元素,取决于实现方式)。

入队(enqueue):在队尾添加元素,队尾指针加1。

出队(dequeue):移除队首元素,队首指针加1(可能需要考虑队列头部的元素移动到尾部以维持连续性,即循环队列)。

链表实现:

使用链表的头部作为队首,尾部作为队尾。

入队:在链表尾部添加新节点。

出队:移除链表头部节点。

应用场景

广度优先搜索(BFS)

操作系统中的任务调度

打印机任务队列

消息队列(如RabbitMQ、Kafka等)

性能特点

数组实现:

时间复杂度:入队、出队平均情况下为O(1),但在最坏情况下(如频繁入队出队导致大量元素移动)可能退化到O(n)。

空间复杂度:O(n),n为队列中元素数量。

优点:随机访问队列中元素速度快。

缺点:可能需要动态扩容,且队列大小受限。

链表实现:

时间复杂度:入队、出队均为O(1)(不考虑链表节点分配的时间)。

空间复杂度:O(n),n为队列中元素数量。

优点:动态大小,不需要预先分配空间。

缺点:无法像数组那样快速随机访问队列中的元素。

总结

栈和队列是两种非常基础且重要的数据结构,它们在算法设计和系统设计中有着广泛的应用。选择数组还是链表实现取决于具体的应用场景和对性能的需求。数组实现通常具有较好的缓存局部性,但大小受限;链表实现则更加灵活,但随机访问性能较差。

 

栈与队列的实现扩展

在数据结构的设计与实现中,数组和链表作为基础但极其重要的元素,广泛应用于各种算法与系统设计中。本文将从栈(Stack)与队列(Queue)的实现出发,深入探讨它们在不同场景下的应用及性能特点,并通过具体的代码示例进行说明。

栈(Stack)的实现

数组实现

数组实现的栈通过维护一个栈顶指针(top)来追踪栈顶元素的位置。初始化时,栈顶指针可以设置为-1(表示空栈)或根据具体情况而定。以下是基于数组的栈的基本操作代码示例(C++):

#include <iostream>

#include <vector>

 

template<typename T>

class StackArray {

private:

std::vector<T> data;

int top = -1;

 

public:

void push(const T& value) {

data.push_back(value);

top++;

}

 

T pop() {

if (isEmpty()) throw std::out_of_range("pop from empty stack");

T value = data[top];

data.pop_back();

top--;

return value;

}

 

T peek() const {

if (isEmpty()) throw std::out_of_range("peek from empty stack");

return data[top];

}

 

bool isEmpty() const {

return top == -1;

}

};

 

// 使用示例

StackArray<int> stack;

stack.push(1);

std::cout << stack.pop() << std::endl; // 输出: 1

链表实现

链表实现的栈通常将链表的头部作为栈顶。这允许我们在O(1)时间复杂度内完成入栈(push)和出栈(pop)操作。以下是基于链表的栈的实现代码(C++):

#include <iostream>

 

template<typename T>

class StackNode {

public:

T value;

StackNode* next;

 

StackNode(T val) : value(val), next(nullptr) {}

};

 

template<typename T>

class StackLinkedList {

private:

StackNode<T>* top = nullptr;

 

public:

void push(const T& value) {

StackNode<T>* newNode = new StackNode<T>(value);

newNode->next = top;

top = newNode;

}

 

T pop() {

if (!top) throw std::out_of_range("pop from empty stack");

T value = top->value;

StackNode<T>* temp = top;

top = top->next;

delete temp;

return value;

}

 

T peek() const {

if (!top) throw std::out_of_range("peek from empty stack");

return top->value;

}

 

bool isEmpty() const {

return top == nullptr;

}

};

 

// 使用示例

StackLinkedList<int> stack;

stack.push(1);

std::cout << stack.pop() << std::endl; // 输出: 1

队列(Queue)的实现

数组实现

数组实现的队列需要维护队首指针(front)和队尾指针(rear)。队列的入队(enqueue)操作在队尾添加元素,出队(dequeue)操作移除队首元素。以下是基于数组的队列实现代码(C++):

// 省略部分代码,因篇幅限制,仅展示关键思路

// 注意:数组实现队列时,需处理队列满和队列空的情况,以及可能的动态扩容

链表实现

链表实现的队列将链表的头部作为队首,尾部作为队尾。这种实现方式允许在O(1)时间复杂度内完成入队和出队操作。以下是基于链表的队列实现代码(C++):

#include <iostream>

 

template<typename T>

class QueueNode {

public:

T value;

QueueNode* next;

 

QueueNode(T val) : value(val), next(nullptr) {}

};

 

template<typename T>

class QueueLinkedList {

private:

QueueNode<T>* front = nullptr;

QueueNode<T>* rear = nullptr;

 

public:

void enqueue(const T& value) {

QueueNode<T>* newNode = new QueueNode<T>(value);

if

 

目录
相关文章
|
3天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
5天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。
|
6天前
|
Java
【数据结构】栈和队列的深度探索,从实现到应用详解
本文介绍了栈和队列这两种数据结构。栈是一种后进先出(LIFO)的数据结构,元素只能从栈顶进行插入和删除。栈的基本操作包括压栈、出栈、获取栈顶元素、判断是否为空及获取栈的大小。栈可以通过数组或链表实现,并可用于将递归转化为循环。队列则是一种先进先出(FIFO)的数据结构,元素只能从队尾插入,从队首移除。队列的基本操作包括入队、出队、获取队首元素、判断是否为空及获取队列大小。队列可通过双向链表或数组实现。此外,双端队列(Deque)支持两端插入和删除元素,提供了更丰富的操作。
11 0
【数据结构】栈和队列的深度探索,从实现到应用详解
|
10天前
|
Linux C++ Windows
栈对象返回的问题 RVO / NRVO
具名返回值优化((Name)Return Value Optimization,(N)RVO)是一种优化机制,在函数返回对象时,通过减少临时对象的构造、复制构造及析构调用次数来降低开销。在C++中,通过直接在返回位置构造对象并利用隐藏参数传递地址,可避免不必要的复制操作。然而,Windows和Linux上的RVO与NRVO实现有所不同,且接收栈对象的方式也会影响优化效果。
|
25天前
|
存储 安全 编译器
缓冲区溢出之栈溢出(Stack Overflow
【8月更文挑战第18天】
46 3
|
12天前
crash —— 获取内核地址布局、页大小、以及栈布局
crash —— 获取内核地址布局、页大小、以及栈布局
|
13天前
|
存储 程序员 C语言
堆和栈之间有什么区别
【9月更文挑战第1天】堆和栈之间有什么区别
84 0
|
26天前
|
测试技术
【初阶数据结构篇】队列的实现(赋源码)
首先队列和栈一样,不能进行遍历和随机访问,必须将队头出数据才能访问下一个,这样遍历求个数是不规范的。
|
1月前
|
算法 C语言 C++
【practise】栈的压入和弹出序列
【practise】栈的压入和弹出序列
|
29天前
栈的几个经典应用,真的绝了
文章总结了栈的几个经典应用场景,包括使用两个栈来实现队列的功能以及利用栈进行对称匹配,并通过LeetCode上的题目示例展示了栈在实际问题中的应用。
栈的几个经典应用,真的绝了