标签:code ret 理解 自己 初始 修改 出队 length 扩展
一、简介
作为一个程序员,算法是一个永远都绕不过去的话题,虽然在大学里参加过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(4) { } /// <summary> /// 初始化指定容量的队列 /// </summary> /// <param name="capacity"></param> public Queue(int capacity) { if (capacity < 0) { throw new Exception("初始容量不能小于0"); } _array = new object[capacity]; _head = 0; _tail = 0; _size = 0; } /// <summary> /// 入队 /// </summary> public virtual void Enqueue(object obj) { _array[_tail] = obj; _tail = _tail + 1; _size++; } /// <summary> /// 出队 /// </summary> /// <returns></returns> public virtual object Dequeue() { if (Count == 0) { throw new InvalidOperationException("当前队列为空,无法执行Dequeue操作"); } object result = _array[_head]; _array[_head] = null; _head = _head + 1; _size--; return result; } /// <summary> /// 当前队列的长度 /// </summary> public int Count { get { return _size; } } }
控制台调用代码如下:
class Program { static void Main(string[] args) { var q = new Queue(); q.Enqueue(1); q.Enqueue(2); q.Enqueue(3); q.Enqueue(4); 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(4) { } /// <summary> /// 初始化指定容量的队列 /// </summary> /// <param name="capacity"></param> public Queue(int capacity) { if (capacity < 0) { throw new Exception("初始容量不能小于0"); } _array = new object[capacity]; _head = 0; _tail = 0; _size = 0; } /// <summary> /// 入队 /// </summary> public virtual void Enqueue(object obj) { if (_array.Length == _size) { int capacity = _array.Length * 2; SetCapacity(capacity); } _array[_tail] = obj; _tail = _tail + 1; _size++; } /// <summary> /// 出队 /// </summary> /// <returns></returns> public virtual object Dequeue() { if (Count == 0) { throw new InvalidOperationException("当前队列为空,无法执行Dequeue操作"); } object result = _array[_head]; _array[_head] = null; _head = _head + 1; _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 = 0; _tail = _size; } }
ok,现在每次都会以原数组*2的长度扩展原始数组,但是还是有问题,如果这中间存在出队,实际的_size会减一,但是数组实际的长度还是为原来的,区别就是出队的那个元素的位置会被设置为null,会存在以下bug:
var q = new Queue(); q.Enqueue(1); q.Dequeue(); q.Enqueue(2); q.Enqueue(3); q.Enqueue(4); q.Enqueue(5); 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.如果你们有好的办法,请告知.
标签:code ret 理解 自己 初始 修改 出队 length 扩展
原文地址:https://www.cnblogs.com/GreenLeaves/p/10177149.html