最近晚上在家里看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++;

浅谈算法和数据结构(1):栈和队列_数据结构

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. 一点点感悟

浅谈算法和数据结构(1):栈和队列_数据结构_02