静态链表
-
地球都知道C语言是个伟大的语言,他的魅力在于指针的灵活性,使得它可以非常容易地操作内存中的地址和数据,这比其他高级语言更加灵活方便。(面向对象使用对象引用机制间接地实现了指针地某些功能)
-
用数组描述地链表叫做静态链表,这种描述方法叫做游标实现法。
/*线性表地静态链表存储结构*/ #define MAXSIZE 1000 typedef struct{ ElemType data; //数据 int cur; //游标(Cursor) }Component,StaticLinkList[MASIZE];
-
对静态链表进行初始化相当于初始化数组:
Status InitList(StaticLinkList space){ int i; for( i = 0 ; i< MAXSIZE-1 ; i++){ space[i].cur = i+1; } space[MAXSIZE-1].cur = 0; return OK; }
-
注意
- 我们对数组的第一个和最后一个元素做特殊处理,他们的data不存放数据。
- 我们通常把未使用的数组元素称为备用链表。
- 数组的第一个元素,即下标为0的那个元素的cur就存放备用链表的第一个结点的下标。
- 数组的最后一个元素,即下标为MAXSIZIE-1的cur则存放第一个有数值的元素的下标,相当于单链表中头结点作用。
静态链表的插入操作
-
静态链表如何模拟单链表进行插入和删除的操作的呢?
- 静态链表要解决的是:如何用静态模拟动态链表结构的存储空间分配,也就是需要的时候申请,不需要的时候释放。
-
在动态链表中,结点的申请和释放分别借用C语言的malloc()和free()两个函数来实现。
-
在静态链表中,操作的是数组,不村存在向动态链表的结点申请和释放的问题,所以我们需要自己实现这两个函数,才可以做到插入和删除操作。
-
为了辨明数组中哪些分量未被使用,解决的办法是将所有未被使用过的及已经删除的游标链成一个备用链表。每当进行插入时,便可以从备用链表上取得第一个结点作为待插入的新结点。
-
静态链表的结构:
-
游标 静态链表特殊的地方,用于存放下一个数据的下标 (第一个游标=最后一个数据的下标)(最后一个数据对应的游标=第一个数据对应的下标(第一个数据为空)) 数据 静态链表存放的数据,数组中的数据 下标 数组下标,用于表示数据在数组中位置(0,1,2,3,……9999)
-
-
代码实现:
-
首先是获得空闲分量的下标:
int Malloc_SLL(StaticLinkList space){ int i = space[0].cur; //获取链表第一个下标,(空表为第二个数据对应的下标,非空表为最后一个数据对应的下标) if(space[0].cur){ space[0].cur = space[i].cur; //将下标对应的游标赋值给第一个游标 //把它的下一个分量用来作为备用 } return i; }
-
插入操作:
/*在静态链表L中第i个元素之前插入新的数据元素e*/ Status ListInsert(StaticLinkList L, int i,ElemType e){ int l,k,j; k = MAXSIZE - 1; //将静态链表的最后一个下标赋值给k if( i<1 || i>ListLength(L)+1){ return ERROR; } j = Malloc_SLL(L); //空表j=1;非空表j为最后一个数据的下标 if(j){ //因为j正常情况下不会为0,所以当j=0时,直接return ERROR L[j].data = e; for( l=1 ; l<=i-1; l++){ k = L[k].cur; } L[j].cur = L[k].cur; L[k].cur = j; return OK; } return ERROR; }
-
静态链表优缺点总结
- 优点:
- 在插入和删除操作时,只需要修改游标,不需要移动元素,从而改进了顺序表存储结构中的插入和删除操作需要移动大量元素的缺点。
- 缺点:
- 没有解决连续存储分配(数组)带来的表长难以确定的问题。
- 失去了顺序存储结构随机存取的特性。
- 总的来说,静态链表其实时为了给没有指针的编程语言设计的一种实现单链表功能的方法。
- 尽管我们可以用单链表就不用静态链表了,但这样的思考方式时非常巧妙地,应该理解其思想,以备不时之需。
单链表小题
-
题目:快速找到未知长度的中间节点。
普通思路:
首先先遍历一遍单链表以确定单链表的长度L。然后再次从头节点出发循环L/2次找到单链表的中间节点。
算法复杂度:O(L+L/2) = O(3L/2)。
慢指针的思路:
利用慢指针原理:设置两个指针* search,* mid都指向单链表的头结点。其中* search的移动速度时* mid的2倍。当* search指向末尾节点的时候,mid正好就在中间了。这就是标尺的思想。
代码实现:
Status GetMidNode(LinkList L,ElemType &e){
LinkList *search,*mid; //定义search和mid两个指针
mid = search = L; //search和mid同时指向单链表
while(search->next != NULL){ //遍历链表到最后一个结点(未完全遍历,剩下最后一个,此时search指向最后一个结点)
//search移动的速度是mid的2倍
if(search->next->next != NULL){ //判断指针search后两个结点是否为空
search = search-next->next; //指针search一次移动两个结点
mid = mid->next; //指针mid一次移动一个结点
}
else{
//判断后两个为空了,就一次移动一个
search = search->next;
}
}
//将单链表中间的元素赋值给e
e = mid->data;
return OK;
}