一、简介

作为一个程序员,算法是一个永远都绕不过去的话题,虽然在大学里参加过ACM的比赛,没记错的话,浙江赛区倒数第二,后来不知怎么的,就不在Care他了,但是现在后悔了,非常的后悔!!!如果当时好好学算法的话,现在去理解一些高深的框架可能会很easy,现在随着C#基础和Web技能的提升,发现哪里都用到算法,但是,很无奈.所以,从今天开始,要重新对自己定位,不能做一个工具的使用者.起码要做到知其所以然.好了,废话不多说,算法之旅,算是正式开始了.希望这个过程能贯穿我的整个职业生涯.甚至整个人生.

二、队列

关于队列,不多说,只要做了一两年程序员,对他肯定不陌生,可以说哪里都有他.关于他的概念也很简单.类似于我们生活中的排队打饭,当然先排队的肯定先打到饭.专业术语叫做先进先出.下面用基于object数组的C#实现,代码如下:

    /// <summary>
/// 自定义队列
/// </summary>
public class Queue
{
private object[] _array; /// <summary>
/// 队列头
/// </summary>
private int _head; /// <summary>
/// 队列尾
/// </summary>
private int _tail; /// <summary>
/// 当前数组的长度
/// </summary>
private int _size; /// <summary>
/// 使用默认构造函数时,给定队列默认的长度4
/// </summary>
public Queue() : this()
{ } /// <summary>
/// 初始化指定容量的队列
/// </summary>
/// <param name="capacity"></param>
public Queue(int capacity)
{
if (capacity < )
{
throw new Exception("初始容量不能小于0");
}
_array = new object[capacity];
_head = ;
_tail = ;
_size = ;
} /// <summary>
/// 入队
/// </summary>
public virtual void Enqueue(object obj)
{
_array[_tail] = obj;
_tail = _tail + ;
_size++;
} /// <summary>
/// 出队
/// </summary>
/// <returns></returns>
public virtual object Dequeue()
{
if (Count == )
{
throw new InvalidOperationException("当前队列为空,无法执行Dequeue操作");
} object result = _array[_head];
_array[_head] = null;
_head = _head + ;
_size--;
return result;
} /// <summary>
/// 当前队列的长度
/// </summary>
public int Count { get { return _size; } }
}

控制台调用代码如下:

    class Program
{
static void Main(string[] args)
{
var q = new Queue();
q.Enqueue();
q.Enqueue();
q.Enqueue();
q.Enqueue();
Console.WriteLine("出队:{0},{1},{2},{3}", q.Dequeue(), q.Dequeue(), q.Dequeue(), q.Dequeue());
Console.ReadKey();
}
}

先进先出,但是有问题,上面给定初始长度为4,所以全局数组的长度为4,当你调用Equeue方法5次,数组会报溢出错误,所以,如果当前队列的长度等于我们给它的初始值时,必须进行一个数组的Copy操作,将当前数组拷贝到一个容量更大的数组中去,这里MS采用的算法时,每次乘以2的递增.修改代码如下:

    /// <summary>
/// 自定义队列
/// </summary>
public class Queue
{
private object[] _array; /// <summary>
/// 队列头
/// </summary>
private int _head; /// <summary>
/// 队列尾
/// </summary>
private int _tail; /// <summary>
/// 当前数组的长度
/// </summary>
private int _size; /// <summary>
/// 使用默认构造函数时,给定队列默认的长度32
/// </summary>
public Queue() : this()
{ } /// <summary>
/// 初始化指定容量的队列
/// </summary>
/// <param name="capacity"></param>
public Queue(int capacity)
{
if (capacity < )
{
throw new Exception("初始容量不能小于0");
}
_array = new object[capacity];
_head = ;
_tail = ;
_size = ;
} /// <summary>
/// 入队
/// </summary>
public virtual void Enqueue(object obj)
{
if (_array.Length == _size)
{
int capacity = _array.Length * ;
SetCapacity(capacity);
}
_array[_tail] = obj;
_tail = _tail + ;
_size++;
} /// <summary>
/// 出队
/// </summary>
/// <returns></returns>
public virtual object Dequeue()
{
if (Count == )
{
throw new InvalidOperationException("当前队列为空,无法执行Dequeue操作");
} object result = _array[_head];
_array[_head] = null;
_head = _head + ;
_size--;
return result;
} /// <summary>
/// 当前队列的长度
/// </summary>
public int Count { get { return _size; } } /// <summary>
/// 重新设定原始数组的容量
/// </summary>
private void SetCapacity(int capacity)
{
var newArray = new object[capacity];
Array.Copy(_array,newArray, _array.Length);
_array = newArray;
_head = ;
_tail = _size;
}
}

ok,现在每次都会以原数组*2的长度扩展原始数组,但是还是有问题,如果这中间存在出队,实际的_size会减一,但是数组实际的长度还是为原来的,区别就是出队的那个元素的位置会被设置为null,会存在以下bug:

            var q = new Queue();
q.Enqueue();
q.Dequeue();
q.Enqueue();
q.Enqueue();
q.Enqueue();
q.Enqueue();
Console.WriteLine("出队:{0},{1},{2},{3}", q.Dequeue(), q.Dequeue(), q.Dequeue(), q.Dequeue());
Console.ReadKey();

出队,导致_size-1,但是原始数组的长度还是为4,千万不要说,Dequeue的时候,让第一个元素的内存释放数组长度变为3,这是不可能的,至少我不知道.所以,这里还需要对算法进行改进.好了,到这里我就做不下去了,看了MS的实现,估计是对数组对了特殊的内存处理,没有办法处理出队后第一个元素为null,但是它还是会算到计算长度里面去,如果引入新的变量去计算实际的长度,不用说,m目测会有内存浪费!mmp.如果你们有好的办法,请告知.

通过学习链表发现,队列可以使用环形链表来实现.关于链表请参考随笔.这里因为MS已经提供了API,所以这里不想继续下去了.如果你理解了链表的原理,通过他来实现队列和栈很简单.但是可能无法实现MS原生的队列那样的效果.

C# 算法系列一基本数据结构的更多相关文章

  1. javascript实现数据结构与算法系列:栈 -- 顺序存储表示和链式表示及示例

    栈(Stack)是限定仅在表尾进行插入或删除操作的线性表.表尾为栈顶(top),表头为栈底(bottom),不含元素的空表为空栈. 栈又称为后进先出(last in first out)的线性表. 堆 ...

  2. 数据结构与算法系列——排序(4)_Shell希尔排序

    1. 工作原理(定义) 希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本.但希尔排序是非稳定排序算法. 希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入 ...

  3. 数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解

    数据结构与算法系列2 线性表 使用java实现动态数组+ArrayList源码详解 对数组有不了解的可以先看看我的另一篇文章,那篇文章对数组有很多详细的解析,而本篇文章则着重讲动态数组,另一篇文章链接 ...

  4. 数据结构与算法系列2 线性表 链表的分类+使用java实现链表+链表源码详解

    数据结构与算法系列2.2 线性表 什么是链表? 链表是一种物理存储单元上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表的链接次序实现的一系列节点组成,节点可以在运行时动态生成,每个节点包括两个 ...

  5. Atitit s2018.6 s6 doc list on com pc.docx Atitit s2018.6 s6 doc list on com pc.docx  Aitit algo fix 算法系列补充.docx Atiitt 兼容性提示的艺术 attilax总结.docx Atitit 应用程序容器化总结 v2 s66.docx Atitit file cms api

    Atitit s2018.6 s6  doc list on com pc.docx Atitit s2018.6 s6  doc list on com pc.docx  Aitit algo fi ...

  6. 玩转算法系列--图论精讲 面试升职必备(Java版)

    第1章 和bobo老师一起,玩转图论算法欢迎大家来到我的新课程:<玩转图论算法>.在这个课程中,我们将一起完整学习图论领域的经典算法,培养大家的图论建模能力.通过这个课程的学习,你将能够真 ...

  7. 简答一波 HashMap 常见八股面试题 —— 算法系列(2)

    请点赞,你的点赞对我意义重大,满足下我的虚荣心. Hi,我是小彭.本文已收录到 GitHub · Android-NoteBook 中.这里有 Android 进阶成长知识体系,有志同道合的朋友,关注 ...

  8. JAVA算法系列 冒泡排序

    java算法系列之排序 手写冒泡 冒泡算是最基础的一个排序算法,简单的可以理解为,每一趟都拿i与i+1进行比较,两个for循环,时间复杂度为 O(n^2),同时本例与选择排序进行了比较,选择排序又叫直 ...

  9. JAVA算法系列 快速排序

    java算法系列之排序 手写快排 首先说一下什么是快排,比冒泡效率要高,快排的基本思路是首先找到一个基准元素,比如数组中最左边的那个位置,作为基准元素key,之后在最左边和最右边设立两个哨兵,i 和 ...

随机推荐

  1. Remote Debugging (2)

    use Eclipse| a Java application 创建一个简单的maven项目 Main.java package cn.zno; public class Main { public ...

  2. 快速排序 JavaScript 实现

    作为算法目录下的第一篇博文,快速排序那是再合适不过了.作为最基本最经典的算法之一,我觉得每个程序员都应该熟悉并且掌握它,而不是只会调用库函数,知其然而不知其所以然. 排序算法有10种左右(或许更多), ...

  3. C#控件之:进度条(ProgressBar)

    一.重绘进度条 public class CustomProgressBar:ProgressBar { public CustomProgressBar() { this.SetStyle(Cont ...

  4. multiprocessing、threading、gevent区别

    1. 进程是资源分配的单位 2. 线程是操作系统调度的单位 3. 进程切换需要的资源很最大,效率很低 4. 线程切换需要的资源一般,效率一般(当然了在不考虑GIL的情况下) 5. 协程切换任务资源很小 ...

  5. MapGIS SDK(C++)【基础篇】

    算法测试:Demo.Test https://www.cnblogs.com/2008nmj/p/10060847.html //例1-1 简单要素 void AppendSFeature(CSFea ...

  6. unigui作中间件使用

    unigui作中间件使用 可返回string或者tstream数据. 如果返回JSON字符,则UNIGUI就是REST 中间件. procedure TUniServerModule.UniGUISe ...

  7. Android-Kotlin-函数表达式&String与Int转换$异常处理

    Kotlin的函数表达式: package cn.kotlin.kotlin_base03 /** * 函数第一种写法 */ fun addMethod1(number1: Int, number2: ...

  8. java的环境变量

    Java学习第一步——JDK安装及Java环境变量配置  2014-05-30 9:09  Java SE  阿超  9226 views Java作为当下很主流的编程语言,学习Java的朋友也越来越 ...

  9. websevice动态控制访问ip

    一般而言webservice是部署在哪台服务器,然后它的address location就是指向哪个,但是由于有些情况处于各种原因,如网络策略,需要先访问某个ip之后再进行跳转到一个ip,这个时候就需 ...

  10. .NetCore 发布到 Centos docker

    [搭建环境] 系统:CentOS 7.0    容器:Docker 1.13.1    .Net Core 版本:.NET Core 2.2     工具:putty.filezilla 等. 一.安 ...