最近晚上在家里看Algorithems,4th Edition,我买的英文版,觉得这本书写的比较浅显易懂,而且“图码并茂”,趁着这次机会打算好好学习做做笔记,这样也会印象深刻,这也是写这一系列文章的原因。另外普林斯顿大学在Coursera 上也有这本书同步的公开课,还有另外一门算法分析课,这门课程的作者也是这本书的作者,两门课都挺不错的。1. 基本概念
现在来看如何实现以上的两个数据结构。在动手之前,Framework Design Guidelines这本书告诉我们,在设计API或者实体类的时候,应当围绕场景编写API规格说明书。
Stack<T>()创建一个空的栈void Push(T s)往栈中添加一个新的元素T Pop()移除并返回最近添加的元素boolean IsEmpty()栈是否为空int Size()栈中元素的个数
栈的链表实现:
classNode
{
publicTItem{get;set;}
publicNodeNext{get;set;}
}
privateNodefirst = null;
privateintnumber = 0;
voidPush(Tnode)
{
NodeoldFirst = first;
first = newNode();
first.Item=node;
first.Next = oldFirst;
number++;
}
栈的数组实现:
intnumber = 0;
publicStackImplementByArray(intcapacity)
{
item = newT[capacity];
}
publicvoidPush(T_item)
{
if(number == item.Length)Resize(2 * item.Length);
item[number++] = _item;
}
default(T);
if(number > 0 && number == item.Length / 4)Resize(item.Length / 2);
returntemp;
}
privatevoidResize(intcapacity)
{
T[]temp = newT[capacity];
for(inti = 0;i < item.Length;i++)
{
temp[i] = item[i];
}
item = temp;
}
2.2 Queue的实现
publicTDequeue()
{
Ttemp = first.Item;
first = first.Next;
number--;
if(IsEmpety())
last = null;
returntemp;
}
voidEnqueue(Titem)
{
NodeoldLast = last;
last = newNode();
last.Item = item;
if(IsEmpety())
{
first = last;
}
else
{
oldLast.Next = last;
}
number++;
voidEnqueue(T_item)
{
if((head - tail + 1) == item.Length)Resize(2 * item.Length);
item[tail++] = _item;
}
publicTDequeue()
{
Ttemp = item[--head];
item[head] = default(T);
if(head > 0 && (tail - head + 1) == item.Length / 4)Resize(item.Length / 2);
在.NET中有Stack和Queue泛型类,使用Reflector工具可以查看其具体实现。先看Stack的实现,下面是截取的部分代码,仅列出了Push,Pop方法,其他的方法希望大家自己使用Reflector查看:
[Serializable,ComVisible(false),DebuggerTypeProxy(typeof(System_StackDebugView<>)),DebuggerDisplay("Count = {Count}"),__DynamicallyInvokable]
publicclassStack<T> : IEnumerable<T>,ICollection,IEnumerable
{
// Fields
privateT[]_array;
privateconstint_defaultCapacity = 4;
privatestaticT[]_emptyArray;
privateint_size;
privateint_version;
// Methods
if(capacity < 0)
{
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity,ExceptionResource.ArgumentOutOfRange_NeedNonNegNumRequired);
}
this._array = newT[capacity];
this._size = 0;
this._version = 0;
}
[__DynamicallyInvokable]
publicvoidCopyTo(T[]array,intarrayIndex)
{
if(array == null)
Array.Reverse(array,arrayIndex,this._size);}
[__DynamicallyInvokable]
publicTPop()
{
if(this._size == 0)
{
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EmptyStack);
}
this._version++;
Tlocal = this._array[--this._size];
this._array[this._size] = default(T);
returnlocal;
returnthis._size;}
}
}
Stack这种数据结构用途很广泛,比如编译器中的词法分析器、Java虚拟机、软件中的撤销操作、浏览器中的回退操作,编译器中的函数调用实现等等。
当发起函数调用的时候,CPU使用特殊的指令将当前的指令指针(instruction pointer),如当前执行的代码的地址压入到堆上。然后CPU通过设置指令指针到函数调用的地址来跳转到被调用的函数去执行。当函数返回值时,旧的指令指针从堆中Pop出来,然后从该指令地址之后继续执行。
线程堆是一块有一定限制的内存空间,如果调用了过多的嵌套函数,或者局部变量分配了过多的内存空间,就会产生堆栈溢出的错误。
1) 当输入的是值的时候Push到属于值的栈中。
4) 当遇到右括号的时候,Pop一个运算符,Pop两个值,然后将计算结果Push到值的栈中。
在Object-C以及OpenGL中都存在”绘图上下文”,有时候我们对局部对象的绘图不希望影响到全局的设置,所以需要保存上一次的绘图状态。下面是Object-C中绘制一个圆形的典型代码:
4.4 一些其他场景
在现实生活中Queue的应用也很广泛,最广泛的就是排队了,”先来后到” First come first service ,以及Queue这个单词就有排队的意思。
5. 一点点感悟