C#数据结构与算法2-C# 栈和队列

news/2025/3/17 19:57:23/文章来源:https://www.cnblogs.com/firespeed/p/18503553

一 栈和队列

栈和队列是非常重要的两种数据结构,在软件设计中应用很多。栈和队列也是线性结构,线性表、栈和队列这三种数据结构的数据元素以及数据元素间的逻辑关系完全相同,差别是线性表的操作不受限制,而栈和队列的操作受到限制。栈的操作只能在表的一端进行,队列的插入操作在表的一端进行而其它操作在表的另一端进行,所以,把栈和队列称为操作受限的线性表。

1 栈

栈(Stack)是操作限定在表的尾端进行的线性表。表尾由于要进行插入、删除等操作,所以,它具有特殊的含义,把表尾称为栈顶( Top),另一端是固定的,叫栈底( Bottom)。当栈中没有数据元素时叫空栈(Empty Stack)。
栈通常记为:S= (a1,a2,…,an),S是英文单词stack的第 1 个字母。a1为栈底元素,an为栈顶元素。这n个数据元素按照a1,a2,…,an的顺序依次入栈,而出栈的次序相反,an第一个出栈,a1最后一个出栈。所以,栈的操作是按照后进先出(Last In First Out,简称LIFO)或先进后出(First In Last Out,简称FILO)的原则进行的,因此,栈又称为LIFO表或FILO表。栈的操作示意图如图所示。

2 BCL中的栈

C#2.0 一下版本只提供了非泛型的Stack类(存储object类型)
C#2.0 提供了泛型的Stack类
重要的方法如下
1,Push()入栈(添加数据)
2,Pop()出栈(删除数据,返回被删除的数据)
3,Peek()取得栈顶的数据,不删除
4,Clear()清空所有数据
4,Count取得栈中数据的个数

3 栈的接口定义

public interface IStack<T> {
int Count{get;}
int GetLength(); //求栈的长度
bool IsEmpty(); //判断栈是否为空
void Clear(); //清空操作
void Push(T item); //入栈操作
T Pop(); //出栈操作
T Peek(); //取栈顶元素
}

4 栈的存储和代码实现-1.1,顺序栈

用一片连续的存储空间来存储栈中的数据元素(使用数组),这样的栈称为顺序栈(Sequence Stack)。类似于顺序表,用一维数组来存放顺序栈中的数据元素。栈顶指示器 top 设在数组下标为 0 的端, top 随着插入和删除而变化,当栈为空时,top=-1。下图是顺序栈的栈顶指示器 top 与栈中数据元素的关系图。

5 栈的存储和代码实现,顺序栈

class SeqStack<T>:IStack<T>

{

private T[] data;

private int top;



public SeqStack(int size)

{

data = new T[size];

top = -1;

}



//默认构造数组的最大容量为10

public SeqStack():this(10)

{

}



public int GetLength()

{

return top + 1;

}


public int Count

{

get

{

return GetLength();

}

}



public bool IsEmpty()

{

return top <= -1;

}



public void Clear()

{

top = -1;

Array.Clear(data,0,data.Length);

}



public void Push(T item)

{

data[top + 1] = item;

top++;

}

/// <summary>

/// 出栈

/// </summary>

/// <returns></returns>

public T Pop()

{

T temp = data[top];

top--;

return temp;

}



public T Peek()

{

return data[top];

}

}

6 栈的存储和代码实现,链栈

栈的另外一种存储方式是链式存储,这样的栈称为链栈(Linked Stack)。链栈通常用单链表来表示,它的实现是单链表的简化。所以,链栈结点的结构与单链表结点的结构一样,如图 3.3 所示。由于链栈的操作只是在一端进行,为了操作方便,把栈顶设在链表的头部,并且不需要头结点。

7 链栈-链栈结点实现

public class Node<T>
{
private T data; //数据域
private Node<T> next; //引用域
//构造器
public Node(T val, Node<T> p)
{
data = val;
next = p;
}

//构造器
public Node(Node<T> p)
{
next = p;
}

//构造器
public Node(T val)
{
data = val;
next = null;
}

//构造器
public Node()
{
data = default(T);
next = null;
}

//数据域属
public T Data
{
get { return data; }
set { data = value; }
}
//引用域属性
public Node<T> Next
{
get { return next; }
set { next = value; }
}
}


8 链栈-代码实现

图片
把链栈看作一个泛型类,类名为 LinkStack。LinkStack类中有一个字段 top 表示栈顶指示器。由于栈只能访问栈顶的数据元素,而链栈的栈顶指示器又不能指示栈的数据元素的个数。所以,求链栈的长度时,必须把栈中的数据元素一个个出栈,每出栈一个数据元素,计数器就增加 1,但这样会破坏栈的结构。为保留栈中的数据元素,需把出栈的数据元素先压入另外一个栈,计算完长度后,再把数据元素压入原来的栈。但这种算法的空间复杂度和时间复杂度都很高,所以,以上两种算法都不是理想的解决方法。理想的解决方法是 LinkStack类增设一个字段 num 表示链栈中结点的个数。

二 队列

队列(Queue)是插入操作限定在表的尾部而其它操作限定在表的头部进行的线性表。把进行插入操作的表尾称为队尾(Rear),把进行其它操作的头部称为队头(Front)。当队列中没有数据元素时称为空队列(Empty Queue)。
队列通常记为:Q= (a1,a2,…,an),Q是英文单词queue的第 1 个字母。a1为队头元素,an为队尾元素。这n个元素是按照a1,a2,…,an的次序依次入队的,出对的次序与入队相同,a1第一个出队,an最后一个出队。所以,对列的操作是按照先进先出(First In First Out)或后进后出( Last In Last Out)的原则进行的,因此,队列又称为FIFO表或LILO表。队列Q的操作示意图如图所示。
在实际生活中有许多类似于队列的例子。比如,排队取钱,先来的先取,后来的排在队尾。
队列的操作是线性表操作的一个子集。队列的操作主要包括在队尾插入元素、在队头删除元素、取队头元素和判断队列是否为空等。与栈一样,队列的运算是定义在逻辑结构层次上的,而运算的具体实现是建立在物理存储结构层次上的。因此,把队列的操作作为逻辑结构的一部分,每个操作的具体实现只有在确定了队列的存储结构之后才能完成。队列的基本运算不
是它的全部运算,而是一些常用的基本运算。

1 BCL中的队列

C#2.0 以下版本提供了非泛型的Queue类
C#2.0 提供了泛型Queue类
方法
1,Enqueue()入队(放在队尾)
2,Dequeue()出队(移除队首元素,并返回被移除的元素)
3,Peek()取得队首的元素,不移除
4,Clear()清空元素
属性
5,Count获取队列中元素的个数

2 队列接口定义

public interface IQueue<T> {
int Count{get;}//取得队列长度的属性
int GetLength(); //求队列的长度
bool IsEmpty(); //判断对列是否为空
void Clear(); //清空队列
void Enqueue(T item); //入队
T Dequque(); //出队
T Peek(); //取队头元素
}

3 顺序队列

用一片连续的存储空间来存储队列中的数据元素,这样的队列称为顺序队列(Sequence Queue)。类似于顺序栈,用一维数组来存放顺序队列中的数据元素。队头位置设在数组下标为 0 的端,用 front 表示;队尾位置设在数组的另一端,用 rear 表示。front 和 rear 随着插入和删除而变化。当队列为空时, front=rear=-1。
图是顺序队列的两个指示器与队列中数据元素的关系图。

4 顺序队列-(循环顺序队列)

如果再有一个数据元素入队就会出现溢出。但事实上队列中并未满,还有空闲空间,把这种现象称为“假溢出”。这是由于队列“队尾入队头出”的操作原则造成的。解决假溢出的方法是将顺序队列看成是首尾相接的循环结构,头尾指示器的关系不变,这种队列叫循环顺序队列(Circular sequence Queue)。循环队列如图所示。

5 顺序队列-代码实现

把循环顺序队列看作是一个泛型类,类名叫 CSeqStack,“ C”是英文单词 circular 的第 1 个字母。CSeqStack类实现了接口 IQueue。用数组来存储循环顺序队列中的元素,在 CSeqStack类中用字段 data 来表示。用字段maxsize 表示循环顺序队列的容量, maxsize 的值可以根据实际需要修改,这通过CSeqStack类的构造器中的参数 size 来实现,循环顺序队列中的元素由 data[0]开始依次顺序存放。字段 front 表示队头, front 的范围是 0 到 maxsize-1。字段 rear表示队尾,rear 的范围也是 0 到 maxsize-1。如果循环顺序队列为空,front=rear=-1。当执行入队列操作时需要判断循环顺序队列是否已满,如果循环顺序队列已满,(rear + 1) % maxsize==front , 循 环 顺 序 队 列 已 满 不 能 插 入 元 素 。所 以 ,CSeqStack类除了要实现接口 IQueue中的方法外,还需要实现判断循环顺序队列是否已满的成员方法。

6 链队列

队列的另外一种存储方式是链式存储,这样的队列称为链队列(Linked Queue)。同链栈一样,链队列通常用单链表来表示,它的实现是单链表的简化。所以,链队列的结点的结构与单链表一样,如图所示。由于链队列的操作只是在一端进行,为了操作方便,把队头设在链表的头部,并且不需要头结点。

7 链队列-链队列结点类

public class Node<T>
{
private T data; //数据域
private Node<T> next; //引用域
//构造器
public Node(T val, Node<T> p)
{
data = val;
next = p;
}
//构造器
public Node(Node<T> p)
{
next = p;
}
//构造器
public Node(T val)
{
data = val;
next = null;
}
//构造器
public Node()
{
data = default(T);
next = null;
}
//数据域属性
public T Data
{
get
{
return data;
}
set
{
data = value;
}
}
//引用域属性
public Node<T> Next
{
get
{
return next;
}
set
{
next = value;
}
}
}


8 链队列-代码实现

把链队列看作一个泛型类,类名为 LinkQueue。LinkQueue类中有两个字段 front 和 rear,表示队头指示器和队尾指示器。由于队列只能访问队头的数据元素,而链队列的队头指示器和队尾指示器又不能指示队列的元素个数,所以,与链栈一样,在 LinkQueue类增设一个字段 num 表示链队列中结点的个数。

9 栈和队列的应用举例

编程判断一个字符串是否是回文。回文是指一个字符序列以中间字符为基准两边字符完全相同,如字符序列“ ACBDEDBCA”是回文。
算法思想:判断一个字符序列是否是回文,就是把第一个字符与最后一个字符相比较,第二个字符与倒数第二个字符比较,依次类推,第 i 个字符与第 n-i个字符比较。如果每次比较都相等,则为回文,如果某次比较不相等,就不是回文。因此,可以把字符序列分别入队列和栈,然后逐个出队列和出栈并比较出队列的字符和出栈的字符是否相等,若全部相等则该字符序列就是回文,否则就不是回文。

 

C# 数据结构与算法 · 目录
上一篇C# 线性表下一篇C# 串和数组
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/822007.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# 托盘通知

一 托盘通知 NotifyIcon托盘通知:程序可以在通知区创建一个通知图标。一般地,可以提示一个气泡通知,右键菜单支持。准备:需要一个图标(*.ico),用于显示在通知区。如果没有ico格式,可以将png图片转成ico。 演示:在项目中添加托盘通知。① 设置文本Text;② 选择图标Icon; …

学习强化学习有哪些工具

强化学习是一种动态的学习方法,目前有许多工具可以帮助研究者和开发者入门和深入学习。主要工具包括:1、OpenAI Gym:一个用于开发和比较强化学习算法的工具包;2、TensorFlow Agents:一个基于TensorFlow的强化学习库;3、Stable Baselines:一个高质量的强化学习库。其中,…

在C语言中如何处理大型项目的模块化

### 在C语言中如何处理大型项目的模块化 在处理大型项目的模块化时,C语言中的关键策略包括使用函数库、分离编译、使用条件编译指令、以及采用模块化设计原则。其中,使用函数库是最直接有效的方法之一,允许开发者重用代码、减少重复工作,并保持代码的整洁和可管理性。 详细…

基类指针、虚纯虚函数、多态性、虚析构

多态 基类指针 // 父类指针可以 new 一个子类对象 Human *pman = new Man(); Human *pwman = new Wonan();抛出问题:父类指针没有办法调用子类的成员函数,那么你为什么还让父类指针 new 一个子类对象呢? 下面与虚函数搭配 虚函数(动态绑定) 我们只定义一个对象指针,就能…

SATA和NVMe SSD在速度上有多大差异

SATA和NVMe SSD在速度上的差异主要体现在:一、传输接口和带宽能力差异;二、读写速度的差异;三、应用场景和效率的差异;四、价格和市场趋势的差异;五、未来发展的差异。实际上,NVMe SSD在速度上远超SATA SSD,特别适合需要高速数据处理的应用场景,如高端游戏、视频编辑和…

Serverless技术栈推荐

# Serverless技术栈推荐 在探讨Serverless技术栈推荐时,我们首先要明确Serverless的核心优势:无需管理服务器、按需自动扩展、成本效益高、开发效率提高。其中,无需管理服务器是Serverless技术的标志性特征,它允许开发者专注于代码和业务逻辑的实现,而无需担心底层基础设施…

Java面试真题之中级进阶(线程,进程,序列化,IO流,NIO)

前言 本来想着给自己放松一下,刷刷博客,慕然回首,线程、程序、进程?Java 序列化?Java 中 IO 流? Java IO与 NIO的区别(补充)?似乎有点模糊了,那就大概看一下Java基础面试题吧。好记性不如烂键盘 *** 12万字的java面试题整理 *** 简述线程、程序、进程的基本概念。以及…

JUC包下的类

1.原子操作类(Atomic) 在Java运算的多线程环境下进行运算若没有进行额外的同步操作,就是线程不安全的多线程并发共享必然会遇到问题所以可以用原子类解决 (1)AtomicInteger 提供了一个原子性的整数变量,可以进行原子性的加减操作。 (2)AtomicLong 提供了一个原子性的长整型…

一般的Java项目需要JVM调优吗_1

在大多数情况下,Java项目不一定需要进行JVM调优。当项目运行稳定、系统资源没有过分消耗时,调优可能是不必要的工作,并且JVM的现代垃圾收集器已被设计得足够智能,适用于大多数场景。然而,在处理大型应用程序、高并发系统,或者面临复杂的性能要求时,JVM调优则成为确保性能…

教你如何在GitHub手机端中怎么下载代码

要在GitHub手机端下载代码,首先要明确你的目标和需求。接下来,我们将详细介绍如何进行这一操作,包括:1. 登录GitHub账号;2. 搜索并定位到目标代码仓库;3. 使用”Download”或”Clone”选项下载;4. 选择合适的格式;5. 确认下载到手机上的位置。手机下载代码的方式较PC端…

蚁群算法的优势在哪里

标题:蚁群算法的优势所在 摘要:蚁群算法,一种模拟蚂蚁觅食行为的启发式算法,因其1、灵活性高、2、适用范围广、3、优化能力强而备受推崇。特别是在解决路径优化问题上,其优化能力强表现尤为突出,通过模拟蚂蚁留下信息素来寻找最短路径,有效地解决了复杂系统中的优化问题…