概念:
单链表的实现很好的借助了指针的作用,但是对一些早期的高级编程语言,他们没有指针,如果想实现单链表,就得用数组来代替指针。
首先我们让数组的元素都是由两个数据域组成,data和cur。也就是说数组的每个下标都对应一个data和一个cur。cur相当于单链表中的next指针,存放该元素的后继在数组中的下标。
我们把这种数组描述的链表叫做静态链表。
实现规则:
对数组的第一个和最后一个元素结点做特殊处理,他们不存放数据。通常把未被使用的数组元素成为备用链表,数组的第一个元素,即下标为0的元素的cur就存放备用链表的第一个结点的下标;而数组的最后一个元素的cur则存放第一个有数值的元素的下标,相当于单链表中的头结点的作用。
当整个链表为空时,如下图所示:
数组的最后一个元素指向第一个有数据的元素的下标,对于空表来说,它的cur等于0,代表后面没有元素,类似于NULL。
而对于有数据的静态链表,如下图所示:
最后一个有值元素,它的游标cur设置为0。同样,如果下标为0的元素的cur等于0,代表没有备用的链表可以使用。
静态链表的插入和删除:
静态链表中要解决的问题是:如何用静态模拟动态链表结构的存储空间的分配,需要的时候申请,不需要的时候释放,类似C语言的malloc和free这两个函数。这里就要自己实现这两个函数,对已用链表和备用链表这两个链表做相应的操作。
现在如果我们需要在“乙”和“丁”之间插入一个值为“丙”的元素,只需要将“乙”的cur改为7,表示下一位是“丙”,并将“丙”的cur改为3,表示下一位是丁。
现在如果我们删除了第一个元素“甲”,表示现在“甲”这个位置空出来了,如果未来有新人要来则优先考虑这里,所以删除的位置成为第一个优先空位,即首元素的cur为1, 下标为1的元素的cur改为8,而下标为8的位置cur改为9,最后元素位置的cur改为2。
代码实现:
#include <iostream> #include <stdlib.h> using namespace std; #define MAXSIZE 100 typedef int ElemType; /*线性表的静态链表存储结构*/ typedef struct { ElemType data; int cur;//为0时表示没有后继的元素,类似于NULL }StaticLinkList[MAXSIZE]; /*将一维数组array中各个分量链成一个备用链表,array[0].cur为头指针,“0”表示空指针*/ bool InitList(StaticLinkList array) { cout << "InitList..." << endl; for(int i = 0; i < MAXSIZE - 2; i++) { array[i].cur = i + 1; } array[MAXSIZE - 2].cur = 0;/*最后一个元素是不可以用的,倒数第二个元素的cur为0,表示备用链表的结尾NULL*/ array[MAXSIZE - 1].cur = 0;/*目前链表为空表,最后一个元素的cur为0*/ return true; } /*若备用链表非空,则返回分配的结点下标,否则返回0*/ int Malloc_SLL(StaticLinkList array) { int i = array[0].cur; if(i != 0) { array[0].cur = array[i].cur;/*下一个空结点用来作备用*/ } return i; } /*将下标为k的空闲结点回收到备用链表*/ void Free_SLL(StaticLinkList array, int k) { array[k].cur = array[0].cur;/*把第一个元素的cur值赋值给要删除元素的cur*/ array[0].cur = k;/*把要删除元素的下标赋值给第一个元素的cur值 */ } /*获取静态链表中元素的个数*/ int ListLength(StaticLinkList array) { int count = 0; int index = array[MAXSIZE - 1].cur; while(index != 0) { count++; index = array[index].cur; }//while return count; } /*在array中第pos个元素之前插入新的数据元素elem*/ bool ListInsert(StaticLinkList array, int pos, ElemType elem) { cout << "Insert List from pos: " << pos << "Item" << elem << endl; if(pos < 1 || pos > ListLength(array) + 1)/*1<=pos<=ListLength(array)+1*/ { return false; } int index = MAXSIZE - 1; int k = Malloc_SLL(array);/*获取空闲分量的下标*/ if(k) { array[k].data = elem; for(int i = 1; i < pos; i++) { index = array[index].cur; } array[k].cur = array[index].cur;/*把第pos个元素之前的那个元素的cur赋值给新元素的cur*/ array[index].cur = k;/*把新元素的下标赋值给第pos个元素之前的元素的cur*/ return true; } return false; } /*删除array中第pos个数据元素*/ bool ListDelete(StaticLinkList array, int pos) { cout << "Delete List from pos: " << pos << endl; if(pos < 1 || pos > ListLength(array)) { return false; } int index = MAXSIZE - 1; for(int i = 1; i < pos; i++)//定位到第pos个元素之前的那个元素 { index = array[index].cur; } int tempIndex = array[index].cur; array[index].cur = array[tempIndex].cur; Free_SLL(array, tempIndex); return true; } bool ListTraverse(StaticLinkList array) { cout << "List Traverse: " << endl; int index = MAXSIZE - 1; while(array[index].cur != 0) { index = array[index].cur; cout << array[index].data << ' '; } cout << endl; return true; } int main(void) { StaticLinkList SLL; InitList(SLL); for(int i= 1; i <11
; i++) { ListInsert(SLL, i, i); } ListTraverse(SLL); ListDelete(SLL, 3); ListTraverse(SLL); cout << "List Length:" << ListLength(SLL) << endl; system("pause"); return 0; }
程序运行结果:
静态链表在插入和删除操作时不需要移动元素,只需要修改游标,从而改进了在顺序结构中插入和删除操作需要移动大量元素的缺点;但是没有解决连续分配存储带来的表长难以确定的问题;并且失去了顺序存储结构随机存取的特性。