数据结构笔记(2)——静态链表

其实这篇文章不是什么重点内容,如果前面的链表理解了可以直接跳过不看。

当语言不直接或者间接提供指针的时候就需要使用静态数组了。其实最核心的思想就是用数组来代替指针,具体做法如下。

首先这里使用的数组是结构体数组,数组元素有两个元素域,datacur,前者存放数据,后者相当于单链表中的next指针,指示下一个地址或者说数组下标。

#define MAXSIZE 1000
typedef struct{
    ElemType data;
    int cur;
}Component, StaticLinkList[MAXSIZE];

声明出来的数组里面第0个和最后一个元素不保存数据,剩下的未被使用的数组元素称为备用链表。最后大概结构是这样的:

数据结构笔记(2)——静态链表_第1张图片

初始化代码:

#define MAXSIZE 1000
typedef struct{
    ElemType data;
    int cur;
}Component, StaticLinkList[MAXSIZE];

Status InitList(StaticLinkList space){
    for(int i; i<MAXSIZE-1; i++){
        space[i].cur = i+1; // 指向下一个结点
    }
    space[MAXSIZE-1].cur = 0;   // 指向回头结点
    return OK;
}

当存入数据的时候,最后一个元素的cur指向第一个有元素的位置,例如下面的例子,最后一个元素的cur为1。

数据结构笔记(2)——静态链表_第2张图片

最后面一个存有数据的元素中cur指向第一个元素,即0,而第0个元素cur的值设置为7,因为从7开始时空闲空间。

静态链表的插入操作

首先,分配空间,因为第0个元素指向的是空闲的空间,所以第0个元素的cur值应当为分配空间后的空闲元素,因此可以这样实现:

int Malloc_SLL(StaticLinkList space){
    int i = space[0].cur;   // 知道当前空闲的空间坐标
    if(space[0].cur){
        space[0].cur = space[i].cur;    // 相当于让指向空闲空间的坐标往后移动一格,
                                        // 这样空出来的位置就可以用来存放新的元素了
    }
    
    return i;
}

函数最后返回空闲位置的下标i

在空闲出来空间之后就可以开始插入操作了。首先回忆一下链表的插入操作是怎么样的,我们先让待插入元素的next指针指向待插入位置的下一个元素,然后让当前位置的前一个元素指向待插入的元素。只是这里cur代表了指针指向的地址,而在静态链表中地址就是数组下标。因此我们可以这样操作:

Status ListInsert(StaticLinkList L, int i, ElemType e){
    int j, k;
    k = MAXSIZE - 1;    // 最后一个元素的下标
    if(i < 1 || i > ListLength(L) + 1){
        return ERROR;
    }
    j = Malloc_SLL(L);  // 获取空闲位置的下标
    if(j){
        L[j].data = e;
        for(int l = 1; l <= i-1; i++){
            k = L[k].cur;   // 遍历到第k-1个元素
        }
        L[j].cur = L[k].cur;    // 先让待插入元素的next指针指向待插入位置的下一个元素
        L[k].cur = j;           // 让当前位置的前一个元素指向待插入的元素
        return OK;
    }
    return ERROR;
}

最终效果有点像下面这张图:

数据结构笔记(2)——静态链表_第3张图片

静态表的删除操作

删除元素需要对应的函数free(),需要这样实现:

Status ListDelete(StaticLinkList L, int i){
    int j, k;
    if(i<1 || i>ListLength(L)){
        return ERROR;
    }
    k = MAXSIZE - 1;    // 最后一个元素
    for(j=1; j<=i-1; j++){
        k = L[k].cur;
    }
    j = L[k].cur;       // 获取最后一个元素的cur值
    L[k].cur = L[j].cur;// 让最后一个元素的cur值指向当前元素的下一个元素
    Free_SSL(L, j);     // 空出第j个位置
    return OK;
}

void Free_SSL(StaticLinkList space, int k){
    space[k].cur = space[0].cur;
    space[0].cur = k;
}

上面的代码思路很简单,因为第0个元素的cur存储的必然是空闲的位置,所以通过将要空闲出来的第k个位置的cur设置为第0个元素记录的空闲元素的位置,这样就可以将所有空闲元素串成一个“备用”链表,相当于就是将空闲位置连在一起了。然后负责存储第一个空闲位置的第0个元素更新cur,让cur指向最新的当前空闲的坐标。

静态链表长度

int ListLength(StaticLinkList L){
    int j = 0;
    int i = L[MAXSIZE-1].cur;		// 获取第一个有数据的元素的位置
    while(i){						// 当i为0的时候说明已经遍历完所有有值元素,此时退出循环
        i = L[i].cur;
        j++;
    }
    return j;
}

最后一个元素的cur指向的是第一个有数据的元素,而最后一个存着数据的元素的cur指向第0个元素,且C语言中0就是false,因此可以通过这种方式来遍历静态链表,计算出链表长度。

一点补充

其实如果从最后一个元素开始遍历静态链表,你会发现链表可以一直无缝从有值元素遍历到空闲元素,也就是说,结构恰好是个环:

数据结构笔记(2)——静态链表_第4张图片
这个特性的实现的前提是空闲元素被“链接”到了一起。

然后说一下静态链表的优缺点:

数据结构笔记(2)——静态链表_第5张图片

参考资料

《大话数据结构》

你可能感兴趣的:(学习笔记,数据结构)