数据结构与算法分析(2)——表、栈和队列

抽象数据类型

    抽象数据类型(ADT)是一系列操作的集合。诸如表、集合、图和他们的操作一起可以看做是抽象数据类型

表 List

表的实现有两种:数组和链表。数组实现的表在插入和删除操作上的花费十分惊人,最坏的情况为O(N),而且数组的大小必须事先指定,意味着表的大小有一个固定的上限,如果这个值很大的话会浪费很大的空间,很小的话又不能满足使用要求,因此表的实现一般采用链表的方式。

链表的节点是一个结构体,结构体内有数据域和下一节点的指针。

struct Node
{
    ElementType Element;  //数据域
    Position Next;        //下一节点指针
}

一般情况下,我们会设置一个表头(Header),表头是个哑节点,只包含了链表第一个元素的地址。这样做的目的是解决在表的第一个元素之前插入元素或者删除表的第一个元素造成的表丢失的风险。给出几个表的基本操作代码。

int isEmpty(List L)
{
    return L->Next == NULL;    //空列表表头的下一个元素不存在,因此测试为真将返回1.
}

int isLast(Position P, List L)
{
    return P->Next == NULL;    //最后一个元素的下个元素不存在,因此测试为真返回1.
}

Position find(ElementType x, List L)
{
    Position p = L->Next;

    while(p != NULL && p->Element != x)
        p = p->Next;

    return p;    //若没有找到,此时的p便为NULL。
}

Position findPrevious(ElementType x, List L)
{
    Position p = L;    //要找x元素的前一个节点,需要从表头开始。

    while(p->Next != NULL && p->Next->Element != x)
        p = p->Next;

    return p;    //若x不存在L中,将返回最后一个元素的位置,最后一个元素不可能是某个节点的前驱。

}

void delete(ElementType x, List L)
{
    Position p, temp;

    p = findPrevious(x,L);    //删除节点是需要用到前驱的指针
    while(p->Next != NULL)    //测试p是不是最后一个节点。可以写一个函数isLast()来进行测试。
    {
        temp = p->Next;
        p->Next = temp->Next;
        free(temp);
    }

//在位置p处插入节点,可以在p前也可在p后,此处采用在p后。
void insert(ElementType x, Position p, List L)    
{
    Position temp;

    temp = malloc(sizeof(Node));    //malloc返回void*类型,未定义类型指针可以指向任何类型。

    if(temp == NULL)
        printerror("No More Space!");
    
    temp->Element = x;
    temp->Next = p->Next;
    p->Next = temp;

}

另外,链表还有双链表以及循环链表,它们的原理与单链表大同小异,这里不多加叙述了。

算法举例(1)——基数排序

未完待续

栈 Stack

栈是限制插入和删除只能在一个位置上进行的表,即只能在栈顶进行操作。基本操作是Push(入栈)和Pop(出栈),可能还有Top(返回栈顶元素)。栈也叫LILO(先进先出)表。一般来说,栈只有栈顶元素是可见的。