一、分析
單鏈表是一種鏈式存取的數據結構,用一組地址任意的存儲單元存放線性表中的數據元素。鏈表中的數據是以結點來表示的,每個結點由元素和指針構成。在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類定義成頭節點類,而其他節點定義成頭節點的內部類,這樣,就只有頭節點可以操作其他節點。
由於要修改的地方太多,這里我就不修改了,放在這里提醒自己。(就是因為懶……(><))
