一、分析
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点由元素和指针构成。在Java中,我们可以将单链表定义成一个类,单链表的基本操作即是类的方法,而结点就是一个个实例化的对象,每个对象中都有“元素值”和“下一结点地址”两个属性。在“下一结点地址”属性中存储的是下一个对象的引用,这样,一个个对象连在一起就成为了单链表。
单链表有以下基本操作:
1、初始化单链表
2、销毁单链表
3、清空单链表
4、检测单链表是否为空
5、返回单链表的元素个数
6、返回单链表中指定位置元素的值
7、返回单链表中第一个与指定值相同的元素的位置
8、返回指定元素的直接前驱
9、返回指定元素的直接后继
10、向指定位置插入元素
11、删除指定位置的元素
12、遍历单链表
为了方便对单链表进行操作,我们还需要引入一个头结点,头结点中不存储元素值,只存储单链表第一个结点的地址。初始化单链表即创建头结点,而销毁单链表即销毁头结点。
二、实现
1、定义类属性和构造函数
1 class InitList{ 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 void clearList() { 2 this.nextList = null; //将头结点的下一结点地址(即单链表的第一个结点的地址)置空,则单链表会因缺少引用而被jvm回收,实现清空 3 }
3、检测单链表是否为空
1 public boolean listEmpty() { 2 if(this.nextList == null) { //通过判断头结点的下一结点地址是否为空,即可判断单链表是否为空 3 return true; 4 } 5 return false; 6 }
4、返回单链表的元素个数
1 public int listLength() { 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(int site) { 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(int value) { 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(int value) { 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 return ret; 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 return ret; 28 }
9、向指定位置插入元素
1 public boolean listInsert(int site,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(int site) { 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 return s; 13 } 14 15 return s.substring(0,s.length() - 1); //去除最后的顿号后返回 16 }
三、小结
以上就是单链表用Java的实现,由于只定义了整数的数组,因此只能操作整数数据,但单链表的基本思想都已实现。
四、纠正
隔了一段时间又回来看代码,猛地发现这段代码其实还不够完善。(⊙x⊙;)
将单链表的基本操作定义成了InitList类的方法,实例化结点时,会使每个结点都拥有这些方法,然而其实只有头结点需要这些方法,其他结点都不需要。
因此可以将InitList类定义成头节点类,而其他节点定义成头节点的内部类,这样,就只有头节点可以操作其他节点。
由于要修改的地方太多,这里我就不修改了,放在这里提醒自己。(就是因为懒……(><))