引言
栈概述
队列概述
实现栈
实现队列
用两个栈实现队列
用一个队列实现栈
总结
引言 栈(Stack)是一种线性数据结构,遵循后进先出(LIFO, Last In, First Out) 原则,最后插入的元素最先被弹出。这意味着插入和删除操作都只在一端进行。而队列(Queue)遵循先进先出(FIFO, First In, First Out) 原则,最先加入队列的元素最先被移除。
栈 :遵循后进先出(LIFO) 。
队列 :遵循先进先出(FIFO) 。
栈概述 栈的核心特性
栈顶(Top) :唯一可进行插入/删除操作的一端。
操作 :
push() 将元素压入栈中
pop() 从栈中移除元素
top() 返回栈顶元素。
isEmpty() 栈为空时返回 true,否则返回 false。
isFull() 栈已满时返回 true,否则返回 false。
栈的应用场景
函数调用管理(调用栈)。
撤销/重做功能。
语法解析(如括号匹配)。
队列概述 队列的核心特性
队头/队尾(Front/Back) :元素从队尾进入,从队头离开。
操作 :
Enqueue(入队):将元素添加到队列的末尾(队尾)。如果队列已满,则发生溢出错误。
Dequeue(出队):从队列的队头移除元素。如果队列为空,则发生下溢错误。
Peek/Front(查看队头):返回队头元素但不移除它。
Size(大小):返回队列中的元素个数。
isEmpty(判空):队列为空时返回 true,否则返回 false。
isFull(判满):队列已满时返回 true,否则返回 false。
队列的应用场景
任务调度(如打印机管理打印任务)。
图算法中的广度优先搜索(BFS)。
数据流缓冲。
队列类型
普通队列(Simple Queue):普通队列遵循 FIFO 结构。我们只能在队尾插入元素,在队头移除元素。普通队列可以使用链表或循环数组高效实现。
双端队列(Deque):在双端队列中,插入和删除操作都可以在两端进行。它分为两种类型: 输入受限队列:这是一种简单队列。在这种类型中,输入只能从一端进行,但删除可以从任意一端进行。 输出受限队列:这也是一种简单队列。在这种类型中,输入可以从两端进行,但删除只能从一端进行。
优先队列(Priority Queue):优先队列是一种特殊队列,元素按照分配的优先级进行访问。它分为两种类型:
升序优先队列:在升序优先队列中,元素按优先级值从小到大排列。优先级值最小的元素最先被弹出。
降序优先队列:在降序优先队列中,元素按优先级值从大到小排列。优先级值最大的元素最先被弹出。
实现栈 基于数组实现栈 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public class ArrayStack <T >{ private T[] _array; private int _topIndex; public ArrayStack (int capacity = 4 ) { _array = new T[capacity]; _topIndex = -1 ; } public void Push (T item ) { if (_topIndex == _array.Length - 1 ) Array.Resize(ref _array, _array.Length * 2 ); _array[++_topIndex] = item; } public T Pop () { if (IsEmpty()) throw new InvalidOperationException("Stack is empty." ); return _array[_topIndex--]; } public T Peek () => IsEmpty() ? throw new InvalidOperationException("Stack is empty." ) : _array[_topIndex]; public bool IsEmpty () => _topIndex == -1 ; }
基于链表实现栈 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Node <T >{ public T Value; public Node<T> Next; public Node (T value ) => Value = value ; } public class LinkedListStack <T >{ private Node<T> _top; public void Push (T item ) { var newNode = new Node<T>(item) { Next = _top }; _top = newNode; } public T Pop () { if (_top == null ) throw new InvalidOperationException("Stack is empty." ); var value = _top.Value; _top = _top.Next; return value ; } public T Peek () => _top == null ? throw new InvalidOperationException("Stack is empty." ) : _top.Value; public bool IsEmpty () => _top == null ; }
实现队列 基于数组实现队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class ArrayQueue <T >{ private T[] _array; private int _front; private int _back; private int _count; public ArrayQueue (int capacity = 4 ) { _array = new T[capacity]; _front = 0 ; _back = 0 ; _count = 0 ; } public void Enqueue (T item ) { if (_count == _array.Length) Array.Resize(ref _array, _array.Length * 2 ); _array[_back % _array.Length] = item; _back++; _count++; } public T Dequeue () { if (IsEmpty()) throw new InvalidOperationException("Queue is empty." ); var value = _array[_front % _array.Length]; _front++; _count--; return value ; } public T Peek () => IsEmpty() ? throw new InvalidOperationException("Queue is empty." ) : _array[_front % _array.Length]; public bool IsEmpty () => _count == 0 ; }
基于链表实现队列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class LinkedListQueue <T >{ private Node<T> _front; private Node<T> _back; public void Enqueue (T item ) { var newNode = new Node<T>(item); if (_back != null ) _back.Next = newNode; _back = newNode; if (_front == null ) _front = _back; } public T Dequeue () { if (_front == null ) throw new InvalidOperationException("Queue is empty." ); var value = _front.Value; _front = _front.Next; if (_front == null ) _back = null ; return value ; } public T Peek () => _front == null ? throw new InvalidOperationException("Queue is empty." ) : _front.Value; public bool IsEmpty () => _front == null ; }
用两个栈实现队列 使用两个栈(inStack 和 outStack)来模拟 FIFO 行为:
inStack 处理入队操作(Enqueue)。
outStack 处理出队操作(Dequeue)。当 outStack 为空时,将 inStack 中的所有元素弹出并压入 outStack,以反转顺序。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class StackToQueue <T > { private Stack<T> _inStack; private Stack<T> _outStack; public StackToQueue () { _inStack = new Stack<T>(); _outStack = new Stack<T>(); } public void Enqueue (T item ) { _inStack.Push(item); } public T Dequeue () { if (IsEmpty()) { throw new InvalidOperationException("Queue is empty" ); } MoveInToOut(); return _outStack.Pop(); } public T Peek () { if (IsEmpty()) { throw new InvalidOperationException("Queue is empty" ); } MoveInToOut(); return _outStack.Peek(); } private void MoveInToOut () { if (_outStack.Count == 0 ) { while (_inStack.Count > 0 ) { _outStack.Push(_inStack.Pop()); } } } public bool IsEmpty () => _inStack.Count + _outStack.Count == 0 ; public int Count => _inStack.Count + _outStack.Count; }
用一个队列实现栈 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class MyStack { Queue<int > myQueue; public MyStack () { myQueue = new Queue<int >(); } public void Push (int x ) { myQueue.Enqueue(x); } public int Pop () { for (var i = 0 ; i < myQueue.Count-1 ; i++) { myQueue.Enqueue(myQueue.Dequeue()); } return myQueue.Dequeue(); } public int Top () { int res = Pop(); myQueue.Enqueue(res); return res; } public bool Empty () { return (myQueue.Count == 0 ); } }
总结
数据结构
逻辑特性
典型场景
基于数组的优势
基于链表的优势
栈
LIFO
表达式求值、撤销操作
访问速度快
无需预分配
队列
FIFO
任务调度、消息队列
内存连续
动态扩展灵活
互相实现的意义
用栈实现队列 :理解 LIFO 和 FIFO 之间的转换,利用栈的特性模拟队列行为。
用队列实现栈 :深入理解数据结构的底层原理,掌握复杂场景下的算法设计。