用数组描述的链表叫作静态链表,这种描述方法叫作游标实现法。数组
如何用数组了描述呢 ?
简单的说,就是咱们会先建立一个固定的数组,而后数组中的每个元素都使用一个结构体,该结构体包括两个元素,一个是要储存的数据,一个是游标。spa
什么是游标?
游标是用来记录链表的下一个结点的位置(节点所在数组中的下标)。code
这都是一些比较抽象的概念,不太好理解,也很容易搞乱,下面我结合这些图简单的说一些。blog
你们能够先创建几个概念,数组和链表可不要搞混了。
首先这个数组,其实就是咱们用来储存用的。那链表呢, 我我的的理解,指得是数组已经使用的这一部分(剩余没使用的那部分暂且叫作备用链表)。咱们使用这个数组下标为0的这个位置来当作链表的头结点,因此说这个位置不进行实际的储存。索引
咱们在进行存储的时候,是按照数组下标的位置把数据逐一存储进数组的(数组的第0个位置不作实际的存储功能,下面会说到),也就是说咱们要把‘甲’,‘乙’,‘丙’,‘丁’,‘戊’,‘己’,‘庚’ 这七个字储存起来,使用的是数组的下标1到6的连续位置,可是在数组中的连续位置,不表明他在链表中的位置,打个比方说,咱们要继续存储一个‘辛’这个字,可是我要把它存到甲和乙中间,他的数组的位置就是在下标为7的位置,可是他的链表位置是在甲和乙中间,这是经过游标来体现出来的,下面会说到rem
好的,咱们分析一下
在初始化完这个数组以后,全部的位置上都没有储存数据,而后每一个位置的游标表示数组的下一个元素,也就是第0个元素的游标为1,第1个元素的游标为2....
进行了存储以后,如上图,咱们看几个比较关键的位置,第0个元素的游标变味了7,第6个元素的游标为0,第999个元素的游标为1,这表明什么意思呢?
咱们在进行插入数据操做的时候,第0个元素的游标老是指向下次储存要用的那个位置,数组的最后一个元素也就是999这个位置的元素,他的游标指向链表的第一个节点(不包括头结点的)在数组中的下标。而后链表的最后一个节点的游标要指向0,由于没有下一个了嘛,因此咱们看到第6个元素的位置的游标是0。it
理论差很少说完了,下面该说具体的实现了,网上的实现代码不少,直接找了一份代码,我们一块儿看一下代码吧io
int main(int argc, const char * argv[]) { StaticLinkList list[MAXSIZE]; // 初始化数组中的数据 createLinkList(list); for (int i = 0; i < 2; i ++) { ListInsert(list, 1, 100+i); } return 0; }
咱们在循环中执行了两次的插入操做,ListInsert
方法中的参数1指的是每次都是把数据插在链表的第1个结点以前,咱们先推测一下结果。for循环
咱们先存了一个100这个数字,又存了101这个数字,由于以前初始化以后并无进行别的存储,因此完成存储以后,由于按照以前的说法,数组的第1个元素应该放的100,第2个位置才是101,可是咱们在储存的时候,是把要插入的数据插入到链表第一个元素的前面,因此从链表的角度看,101应该是第一个节点,100是第二个。那要怎么表现出来呢?结合咱们上面说的,999这个位置的游标应该指向链表第一个节点,也就是游标应该为2,数组下标2的元素由于他是链表的第一个节点,因此他的游标应该指向链表第二个节点,也就是数组下标1。链表就两个节点,因此最后一个节点的游标要指向0. 而后数组下标0的位置的游标指向了下一个可用的存储位置3。class
结果跟咱们以前分析的是一致的。
只要理清楚了这些思路以后,其余的代码看起来也就容易不少了。
好比说下面这个计算链表长度的方法
int linkLength(StaticLinkList list[]) { int j = 0; int i = list[MAXSIZE-1].cur; while (i) { i = list[i].cur; j ++; } return j; }
先拿到数组的最后一个元素游标的数值,若是是0,就说明尚未储存呢,索引链表长度就是0,若是已经储存过了,因此获得i的值就不是0,经过一个while循环,一个个的遍历链表结点,直到链表最后一个结点的游标又是0,跳出循环,而后经过一个j来标识循环次数,也就是链表长度。
关于静态链表就说这么多吧,更多的内容你们能够上网搜一下。
下面我把使用的代码放上,主要是实现了插入操做,删除暂时没作,你们能够看懂了插入而后本身写一下删除,代码上我尽量详细的作了注释,你们能够看看代码 ~
// main.c // 链表 // Created by sunxiaobin on 2018/6/15. #include <stdio.h> #define MAXSIZE 1000 typedef int ElemType; typedef struct { ElemType data; int cur; }StaticLinkList; // 声明 void createLinkList(StaticLinkList list[]); int linkLength(StaticLinkList list[]); int Malloc_SLL(StaticLinkList list[]); int ListInsert(StaticLinkList L[], int i, ElemType e); int main(int argc, const char * argv[]) { StaticLinkList list[MAXSIZE]; // 初始化数组中的数据 createLinkList(list); for (int i = 0; i < 2; i ++) { ListInsert(list, 1, 100+i); } return 0; } void createLinkList(StaticLinkList list[]){ int i;// i就表明数组下标 for (i = 0; i < MAXSIZE-1; i ++) { list[i].cur = i + 1; // cur存放的是下一个结点的下标 } list[MAXSIZE-1].cur = 0; // } // 求链表长度 int linkLength(StaticLinkList list[]) { int j = 0; int i = list[MAXSIZE-1].cur; while (i) { i = list[i].cur; j ++; } return j; } // 获得备用链表的第一个元素下标 int Malloc_SLL(StaticLinkList list[]) { int i = list[0].cur; if (list[0].cur) // 更新头结点的游标, 原来头结点的游标指向i这个位置, // 可是这个位置要进行了存储 , 被使用了, // 头结点应该把游标更新到新的备用链表的第一个元素, 也就是刚刚使用了这个i位置的元素的游标指向的位置. list[0].cur = list[i].cur; return i; } // 在静态链表L中第i个元素以前插入新的数据元素e int ListInsert(StaticLinkList list[], int i, ElemType e) { int k = MAXSIZE - 1; // 数组的最后一个元素 //判断要插入的位置是否是合法范围 if (i<1 || i>linkLength(list)+1) return 0; int j = Malloc_SLL(list); // 获得备用链表的第一个元素下标 if (j) { // 若是元素存在,(不存在就说明数组已经满了) list[j].data = e;// 先把e存进来,而后经过改变某几个结点的游标 // 这个for循环内, 若是是第一次存储的话根本不走 // for (int l = 1; l<=i-1; l++) { // 获得第i-1个结点在数组中的下标 k = list[k].cur; } list[j].cur = list[k].cur; // 将第i-1个元素的cur设置为新加的这个结点的下标,将新加的这个结点的下标设置为以前第i-1个元素存储的cur值 list[k].cur = j; return 1; } return 0; }