Java数据结构——静态链表实现
一、定义
用数组描述的链表,即称为静态链表。
在C语言中,静态链表的表现形式即为结构体数组,结构体变量包括数据域data和游标CUR。(这里的定义来自于百度百科)
二、优点
(1)这种存储结构,仍需要预先分配一个较大的空间,但在作为线性表的插入和删除操作时不需移动元素,仅需修改指针,故仍具有链式存储结构的主要优点。
(2)假如有如上的静态链表S中存储这线性表(a,b,c,d,f,g,h,i),Maxsize=11,如图所示,要在第四个元素后插入元素e,方法是:先在当前表尾加入一个元素e,即:S[9].data = e;然后修改第四个元素的游标域,将e插入到链表中,即:S[9].cursor = S[4].cursor; S[4].cursor = 9;,接着,若要删除第7个元素h,则先顺着游标链通过计数找到第7个元素存储位置6,删除的具体做法是令S[6].cursor = S[7].cursor。
三、原理图
例如一组数据如下:
(ZHAO,QIAN,SUN,LI,ZHOU,WU,ZHENG,WANG)
1 /** 2 * 静态链表的长度 3 * @return 4 */ 5 public int length(){ 6 return size-1; 7 }
因为有个空表头,所以长度为size-1;
1 /** 2 * 静态链表的初始化 3 */ 4 public StaticLinkList(){ 5 linkList = new Element[DEFAULT_SIZE]; 6 for (int i = 0; i < linkList.length; i++) { 7 linkList[i] = new Element(); 8 linkList[i].data = -1; 9 linkList[i].cur = i+1; 10 } 11 //当前空闲节点从1开始,因为第0个节点设置成了头结点,设置为空,不 12 //存储数据 13 currentFree = 1; 14 }
这里是不指定链表大小的链表初始化,默认长度为DEFAULT_SIZE,第8行为链表数据初始化,第9行初始化游标cur。
1 /** 2 * 给链表添加数据,每当链表满了就给链表添加额外的空间 3 * @param data 4 */ 5 public void add(int data){ 6 if(size<linkList.length){ 7 linkList[currentFree].data = data; 8 currentFree = linkList[currentFree].cur; 9 size++; 10 }else{//链表已满,给链表添加空间 11 addLinkSpace(); 12 linkList[currentFree].data = data; 13 currentFree = linkList[currentFree].cur; 14 size++; 15 } 16 }
这个函数是给静态链表添加数据,第6行判断链表是否已满,currentFree是指向当前空闲位置,第7行是将要添加的数据添加到空闲位置上,第8行currentFree指向下一个空闲位置,第9行链表实际长度加1,第11行,如果链表已满,则给链表添加空间(addLinkSpace())。
1 /** 2 * 得到索引指定的数据 3 * @param index 4 * @return 5 */ 6 public int get(int index){ 7 if(index>size-1&&index<0) 8 throw new IndexOutOfBoundsException("数组越界,索引不合法"); 9 else{ 10 //这里index+1也是因为多了一个空的头节点 11 return linkList[index+1].data; 12 } 13 }
第11行index+1是因为链表的第一位为空,不取到第一位。
1 /** 2 * 删除指定位置的节点 3 * @param index 4 */ 5 public void delete(int index){ 6 7 index = index+1; 8 if(index<1||index>=size){ 9 System.out.println("超出链表长度"); 10 }else if(index == size-1){ 11 size--; 12 linkList = (Element[]) getTrueIndex(linkList,size); 13 }else{ 14 int i = 0; 15 while(index!=linkList[i].cur){ 16 i++; 17 } 18 int j = 0; 19 while(currentFree!=linkList[j].cur){ 20 j++; 21 } 22 linkList[i].cur = linkList[index].cur; 23 linkList[j].cur = index; 24 linkList[index].cur = currentFree; 25 currentFree = index; 26 size--; 27 linkList = (Element[]) getTrueIndex(linkList,size); 28 } 29 }
第7行也是因为链表头结点为空。第10行是指,如果删除链表的最后一位,则执行下面的操作。第12行是将链表按照逻辑顺序重新排列,是为了显示。第14-17行是为了找到指向index的游标,如果没有12行,则指向index的游标不一定是index-1,因为物理位置连续的,逻辑位置不一定连续。18-21行是为了找到指向当前空闲位置的游标,理由同上。22行是将原指向index的游标指向原index的游标指向的位置,23行是将原指向当前空闲位置的游标指向index,表明index为空闲,24行是将index的游标指向原当前空闲位置,25行是将当前空闲位置指向index。其他的都是差不多的,还有几个函数就不一一介绍了。
四、完整代码示例
4.1 StaticLinkList.java
package com.yds.list; /** * 因为静态链表实质上是一维数组的存储方式,所以它在物理位置上的存储 * 是顺序的,但它是用游标来指向下一个数据的,所以根据它的下标来获取数据 * 和按照游标的指向来获取数据是不同的,这里设置该链表的头结点为空 * @author Administrator * */ public class StaticLinkList { private Element[] linkList = null; // private Element[] linkList1 = null; private int DEFAULT_SIZE = 4;//默认存储大小 private int currentFree = 0;//指向当前空闲位置 private int size = 1; class Element{ int data; int cur; } /** * 静态链表的长度 * @return */ public int length(){ return size-1; } /** * 静态链表的初始化 */ public StaticLinkList(){ linkList = new Element[DEFAULT_SIZE]; for (int i = 0; i < linkList.length; i++) { linkList[i] = new Element(); linkList[i].data = -1; linkList[i].cur = i+1; } //当前空闲节点从1开始,因为第0个节点设置成了头结点,设置为空,不 //存储数据 currentFree = 1; } /** * 给链表添加数据,每当链表满了就给链表添加额外的空间 * @param data */ public void add(int data){ if(size<linkList.length){ linkList[currentFree].data = data; currentFree = linkList[currentFree].cur; size++; }else{//链表已满,给链表添加空间 addLinkSpace(); linkList[currentFree].data = data; currentFree = linkList[currentFree].cur; size++; } } /** * 得到索引指定的数据 * @param index * @return */ public int get(int index){ if(index>size-1&&index<0) throw new IndexOutOfBoundsException("数组越界,索引不合法"); else{ //这里index+1也是因为多了一个空的头节点 return linkList[index+1].data; } } /** * 删除指定位置的节点 * @param index */ public void delete(int index){ index = index+1; if(index<1||index>=size){ System.out.println("超出链表长度"); }else if(index == size-1){ size--; linkList = (Element[]) getTrueIndex(linkList,size); }else{ int i = 0; while(index!=linkList[i].cur){ i++; } int j = 0; while(currentFree!=linkList[j].cur){ j++; } linkList[i].cur = linkList[index].cur; linkList[j].cur = index; linkList[index].cur = currentFree; currentFree = index; size--; linkList = (Element[]) getTrueIndex(linkList,size); } } /** * 增加链表空间 */ public void addLinkSpace(){ DEFAULT_SIZE+=8; Element[] link = linkList; linkList = new Element[DEFAULT_SIZE]; System.arraycopy(link, 0, linkList, 0, link.length); for (int i = link.length; i < DEFAULT_SIZE; i++) { linkList[i] = new Element(); linkList[i].data = -1; linkList[i].cur = i+1; } currentFree = link.length; } /** * 根据指定的位置插入数据 * @param index * @param data */ public void insert(int index,int data){ //这里加1的原因是因为链表的第0位为空节点,这里设置的头节点为空 index = index + 1; if(size<linkList.length){ if(index>size&&index<0) System.out.println("数组越界,超出数组长度"); else if(index == size){ linkList[currentFree].data = data; currentFree = linkList[currentFree].cur; size++; }else{ /******未按逻辑顺序排序而插入数据的写法,因为未排序,则当前索引的上个节点的索引不一定是当前索引减1****/ int i = 0; while(index!=linkList[i].cur){ i++; } int j = 0; while(currentFree!=linkList[j].cur){ j++; } linkList[i].cur = currentFree; linkList[j].cur = linkList[currentFree].cur; linkList[currentFree].data = data; linkList[currentFree].cur = index; currentFree = linkList[j].cur; size++; //每次插入后将链表按逻辑顺序重新排序,是为了方便输出查看。 linkList = (Element[]) getTrueIndex(linkList,size); } }else{ addLinkSpace(); insert(index, data); } } /** * 按照逻辑顺序重新排列 * @param link * @return */ public Object getTrueIndex(Element[] link,int size){ Element[] linkList1 = new Element[linkList.length]; int k =0; for (int i = 0; i < linkList.length; i++) { linkList1[i] = new Element(); linkList1[i].data = link[k].data; k = link[k].cur; linkList1[i].cur = i+1; } //插入时,currentFree肯定是最后一个了,但删除后,currentFree就不一定是最后一位了 currentFree = size; return linkList1; } }
4.2 JavaMain.java
package com.yds.list; public class JavaMain { public static void main(String[] args) { // TODO Auto-generated method stub StaticLinkList listA = new StaticLinkList(); int la[]={ 2,3,5,8,9,6,7 }; for (int i = 0; i < la.length; i++) { listA.add(la[i]); } listA.delete(6); listA.delete(0); listA.insert(3, 88); listA.insert(3, 78); for (int i = 0; i < listA.length(); i++) { System.out.println(listA.get(i)); } } }
五、结果截图