上一篇 完成了基本的线性表部分的介绍,本篇开始学习一个特殊的线性表结构:栈 。 1栈的初体验
现实生活中的事情往往都能总结归纳成一定的数据结构,例如餐馆中餐盘的堆叠和使用,羽毛球筒里装的羽毛球等都是典型的栈结构。而在.NET中,值类型在线程栈上进行分配,引用类型在托管堆上进行分配,本文所说的“栈”正是这种数据结构。栈和队列都是常用的数据结构,它们的逻辑结构与线性表相通,不同之处则在于操作受某种特殊限制。因此,栈和队列也被称为操作受限的线性表。这里,我们首先来了解一下栈。
栈的基本特征
栈(stack)是限定仅在表尾进行插入和删除操作的线性表。其特点是:“后进先出“或“先进后出”。
栈的基本操作
(1)栈的插入操作,叫作进栈,也称压栈、入栈:
(2)栈的删除操作,叫作出栈,也有的叫作弹栈:
2栈的基本实现(顺序存储) 既然栈属于特殊的线性表,那么其实现也会有两种形式:顺序存储结构和链式存储结构。首先,对于Stack,我们希望能够提供以下几个方法供调用:Stack() | 创建一个空的栈 |
void Push(T s) | 往栈中添加一个新的元素 |
T Pop() | 移除并返回最近添加的元素 |
bool IsEmpty() | 栈是否为空 |
int Size() | 栈中元素的个数 |
对于顺序存储,我们可以参照顺序表的实现方式,借助数组来存储各个数据元素,然后对这个数组进行一定的封装,提供指定的操作对数据元素进行插入和删除即可。
(1)入栈操作实现
代码如下:
/// /// 入栈/// /// 节点元素public void Push(T node){ if (index == nodes.Length) { // 增大数组容量 ResizeCapacity(nodes.Length * 2); } nodes[index] = node; index++;}
借助数组来实现入栈操作,其关键之处就在于top指针的移动。这里index初始值为0,每次入栈一个则将index加1,即指向下一个即将入栈的位置。由于这里采用了动态扩容的机制,所以没有判断栈中元素个数是否达到了最大值。
(2)出栈操作实现
出栈操作需要先去的要出栈的元素,然后将index减1,即指向下一个即将出栈的元素的位置。
/// /// 出栈/// /// 出栈节点元素public T Pop(){ if(index == 0) { return default(T); } T node = nodes[index - 1]; index--; nodes[index] = default(T); if (index > 0 && index == nodes.Length / 4) { // 缩小数组容量 ResizeCapacity(nodes.Length / 2); } return node;}
这里首先需要判断index是否已经到达了最小值,出栈的元素位置需要置为默认值(如果是int数组,那么会重置为0),最后返回出栈的元素对象。这里当元素个数小于数组的四分之一时会进行容量收缩操作。
(3)完整的类实现
/// /// 基于数组的栈实现/// /// 类型public class MyArrayStack{ private T[] nodes; private int index; public MyArrayStack(int capacity){ this.nodes = new T[capacity]; this.index = 0; } /// /// 入栈 /// /// 节点元素 public void Push(T node){ if (index == nodes.Length) { // 增大数组容量 ResizeCapacity(nodes.Length * 2); } nodes[index] = node; index++; } /// /// 出栈 /// /// 出栈节点元素 public T Pop(){ if(index == 0) { return default(T); } T node = nodes[index - 1]; index--; nodes[index] = default(T); if (index > 0 && index == nodes.Length / 4) { // 缩小数组容量 ResizeCapacity(nodes.Length / 2); } return node; } /// /// 重置数组大小 /// /// 新的容量 private void ResizeCapacity(int newCapacity){ T[] newNodes = new T[newCapacity]; if(newCapacity > nodes.Length) { for (int i = 0; i < nodes.Length; i++) { newNodes[i] = nodes[i]; } } else { for (int i = 0; i < newCapacity; i++) { newNodes[i] = nodes[i]; } } nodes = newNodes; } /// /// 栈是否为空 /// /// true/false public bool IsEmpty(){ return this.index == 0; } /// /// 栈中节点个数 /// public int Size { get { return this.index; } }}
(4)简单的功能测试
首先,顺序入栈10个随机数,输出其元素个数与是否为空;
然后依次出栈,输出每个数据元素;
最后,再入栈15个随机数并出栈输出。
/// /// 基于数组的栈的测试/// static void StackWithArrayTest(){ MyArrayStack<int> stack = new MyArrayStack<int>(10); Console.WriteLine(stack.IsEmpty()); Random rand = new Random(); for (int i = 0; i < 10; i++) { stack.Push(rand.Next(1, 10)); } Console.WriteLine("IsEmpty:{0}",stack.IsEmpty()); Console.WriteLine("Size:{0}", stack.Size); Console.WriteLine("-------------------------------"); for (int i = 0; i < 10; i++) { int node = stack.Pop(); Console.Write(node + " "); } Console.WriteLine(); Console.WriteLine("IsEmpty:{0}", stack.IsEmpty()); Console.WriteLine("Size:{0}", stack.Size); Console.WriteLine("-------------------------------"); for (int i = 0; i < 15; i++) { stack.Push(rand.Next(1, 15)); } for (int i = 0; i < 15; i++) { int node = stack.Pop(); Console.Write(node + " "); } Console.WriteLine();}
运行结果如下所示:
3小结本文介绍了栈的基本概念及顺序存储的实现,下一篇会介绍链式存储的实现!
4参考资料 程杰,《大话数据结构》 陈广,《数据结构(C#语言描述)》 段恩泽,《数据结构(C#语言版)》 往期 精彩 回顾每天5分钟用C#学习数据结构(9)
每天5分钟用C#学习数据结构(8)
每天5分钟用C#学习数据结构(7)
基于Jenkins的开发测试全流程持续集成实践
基于Jenkins的ASP.NET Core持续集成实践
点个“在看”/转发朋友圈就是对我最大的支持
?点击