一、分析
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点由元素和指针构成。在Java中,我们可以将单链表定义成一个类,单链表的基本操作即是类的方法,而结点就是一个个实例化的对象,每个对象中都有“元素值”和“下一结点地址”两个属性。在“下一结点地址”属性中存储的是下一个对象的引用,这样,一个个对象连在一起就成为了单链表。
单链表有以下基本操作:
1、初始化单链表
2、销毁单链表
3、清空单链表
4、检测单链表是否为空
5、返回单链表的元素个数
6、返回单链表中指定位置元素的值
7、返回单链表中第一个与指定值相同的元素的位置
8、返回指定元素的直接前驱
9、返回指定元素的直接后继
10、向指定位置插入元素
11、删除指定位置的元素
12、遍历单链表
为了方便对单链表进行操作,我们还需要引入一个头结点,头结点中不存储元素值,只存储单链表第一个结点的地址。初始化单链表即创建头结点,而销毁单链表即销毁头结点。
二、实现
1、定义类属性和构造函数
1 classInitList{2
3 private int [] data = new int[1]; //用来存储元素值,之所以用数组而不用整型,是为了用null来表示头结点
4
5 private InitList nextList; //下一结点地址
6
7 public InitList() { //创建头结点的构造函数
8 this.data = null;9 this.nextList = null;10 }11
12 public InitList(int data) { //创建普通结点的构造函数
13 this.data[0] =data;14 this.nextList = null;15 }16 }
2、清空单链表
1 public voidclearList() {2 this.nextList = null; //将头结点的下一结点地址(即单链表的第一个结点的地址)置空,则单链表会因缺少引用而被jvm回收,实现清空
3 }
3、检测单链表是否为空
1 public booleanlistEmpty() {2 if(this.nextList == null) { //通过判断头结点的下一结点地址是否为空,即可判断单链表是否为空
3 return true;4 }5 return false;6 }
4、返回单链表的元素个数
1 public intlistLength() {2
3 InitList theList = this.nextList; //获取头结点的下一结点地址
4 int i = 0; //计数器初始化
5
6 for (i = 0; theList != null; i++) { //循环判断结点地址是否为空,如果不为空,则表明存在结点,计数器i加一;如果为空,则表明已到达单链表尾部,退出循环
7 theList = theList.nextList; //取下一结点进行判断
8 }9 return i; //返回计数器的值
10 }
5、返回单链表中指定位置元素的值
1 public int [] getElem(intsite) {2
3 if(site < 1) { //判断输入的位置是否合法
4 return null;5 }6
7 InitList theList = this; //得到头结点的地址
8
9 for (int i = 0; i < site; i++) { //循环读取结点,直到指定的位置
10 theList = theList.nextList; //获取下一结点的地址
11 if(theList == null) { //如果下一结点地址为空,则表明已经到达单链表末尾,指定的位置超出了单链表的长度
12 return null; //未取到元素,返回null
13 }14 }15 return theList.data; //返回指定位置元素值
16 }
6、返回单链表中第一个与指定值相同的元素的位置
1 public int locateElem(intvalue) {2
3 InitList theList = this.nextList;
4
5 for(int i = 1; theList != null; i++) { //如果取得的结点不为空,执行循环
6 if(theList.data[0] == value) { //比较结点值与给定的值是否相等
7 return i; //相等返回结点位置
8 }9 theList = theList.nextList; //取下一结点地址
10 }11
12 return 0; //未找到则返回零
13 }
7、返回指定元素的直接前驱
1 public int [] priorElem(intvalue) {2
3 InitList theList = this.nextList;4
5 if(theList == null) { //如果头结点的下一结点为空,则表明单链表为空,返回null
6 return null;7 }8
9 InitList theNextList = this.nextList.nextList; //获取单链表的第二个结点
10 int [] ret = new int[this.listLength()]; //创建一个与单链表长度相同的数组,用来存储找到的直接前驱的值
11 int i = 1; //计数器
12
13 while (theNextList != null) { //因为单链表的第一个结点没有直接前驱,因此从第二个结点开始循环
14 if(theNextList.data[0] == value) { //如果与给定值相等,则取得其前驱,计数器加一
15 ret[i] = theList.data[0];16 i++;17 }18 theList = theNextList; //取下一地址,准备下一循环
19 theNextList =theNextList.nextList;20 }21
22 if(i == 1) { //i为1表明未取到直接前驱
23 return null;24 }25
26 ret[0] = i - 1; //将计数器的值存入数组第0位
27 returnret;28 }
8、返回指定元素的直接后继
1 public int [] nextElem(int value) { //与获取直接前驱类似,这里不再赘述
2
3 InitList theList = this.nextList;4
5 if(theList == null) {6 return null;7 }8
9 InitList theNextList = this.nextList.nextList;10 int [] ret = new int[this.listLength()];11 int i = 1;12
13 while (theNextList != null) {14 if(theList.data[0] ==value) {15 ret[i] = theNextList.data[0];16 i++;17 }18 theList =theNextList;19 theNextList =theNextList.nextList;20 }21
22 if(i == 1) {23 return null;24 }25
26 ret[0] = i - 1;27 returnret;28 }
9、向指定位置插入元素
1 public boolean listInsert(intsite,int value) {2
3 if(site < 1) { //判断指定位置是否合法
4 return false;5 }6
7 InitList list = new InitList(value);
8 InitList theNextList = this;9 InitList theList = null;10
11 for(int i = 0; i < site; i++) { //循环读取到指定位置
12 theList =theNextList;13 if(theList == null) { //如果为空,表示已到单链表末尾,返回false
14 return false;15 }16 theNextList =theNextList.nextList;17 }18
19 list.nextList = theNextList; //将新结点插入指定位置中
20 theList.nextList =list;21 return true;22 }
10、删除指定位置的元素
1 public boolean listDelete(intsite) {2
3 InitList theList = this;4 InitList theNextList = this.nextList;5
6 if(site < 1 || theNextList == null) { //判断指定位置是否合法和单链表是否为空
7 return false;8 }else if(site == 1) { //如果要删除的是第一个结点,则直接删除
9 theList.nextList =theNextList.nextList;10 return true;11 }12
13 for(int i = 1; i < site; i++) { //循环读取到指定位置
14 theNextList =theNextList.nextList;15 if(theNextList == null) {16 return false;17 }18 theList =theList.nextList;19 }20
21 theList.nextList = theNextList.nextList; //删除指定位置的结点
22 return true;23 }
11、遍历单链表
1 public String traverseList() { //这里通过输出单链表来表示遍历
2
3 InitList theList = this.nextList;4 String s = ""; //用来存储单链表的值
5
6 while(theList != null) { //循环获取结点值
7 s += theList.data[0] + "、";8 theList =theList.nextList;9 }10
11 if(s.length() == 0) { //如未获取到值,直接返回s
12 returns;13 }14
15 return s.substring(0,s.length() - 1); //去除最后的顿号后返回
16 }
三、小结
以上就是单链表用Java的实现,由于只定义了整数的数组,因此只能操作整数数据,但单链表的基本思想都已实现。
四、纠正
隔了一段时间又回来看代码,猛地发现这段代码其实还不够完善。(⊙x⊙;)
将单链表的基本操作定义成了InitList类的方法,实例化结点时,会使每个结点都拥有这些方法,然而其实只有头结点需要这些方法,其他结点都不需要。
因此可以将InitList类定义成头节点类,而其他节点定义成头节点的内部类,这样,就只有头节点可以操作其他节点。
由于要修改的地方太多,这里我就不修改了,放在这里提醒自己。(就是因为懒……(><))