java實現--單向連結串列的插入和刪除
一、連結串列結構: (物理儲存結構上不連續,邏輯上連續;大小不固定)
概念:
鏈式儲存結構是基於指標實現的。我們把一個數據元素和一個指標稱為結點。
資料域:存數資料元素資訊的域。
指標域:儲存直接後繼位置的域。
鏈式儲存結構是用指標把相互直接關聯的結點(即直接前驅結點或直接後繼結點)連結起來。鏈式儲存結構的線性表稱為連結串列。
連結串列型別:
根據連結串列的構造方式的不同可以分為:
- 單向連結串列
- 單向迴圈連結串列
- 雙向迴圈連結串列
二、單鏈表:
概念:
連結串列的每個結點中只包含一個指標域,叫做單鏈表(即構成連結串列的每個結點只有一個指向直接後繼結點的指標)
單鏈表中每個結點的結構:
1、頭指標和頭結點:
單鏈表有帶頭結點結構和不帶頭結點結構兩種。
“連結串列中第一個結點的儲存位置叫做頭指標”,如果連結串列有頭結點,那麼頭指標就是指向頭結點的指標。
頭指標所指的不存放資料元素的第一個結點稱作頭結點(頭結點指向首元結點)。頭結點的資料域一般不放資料(當然有些情況下也可存放連結串列的長度、用做監視哨等)
存放第一個資料元素的結點稱作第一個資料元素結點,或稱首元結點。
如下圖所示:
不帶頭結點的單鏈表如下:
帶頭結點的單鏈表如下圖:
關於頭指標和頭結點的概念區分,可以參考如下部落格:
2、不帶頭結點的單鏈表的插入操作:
上圖中,是不帶頭結點的單鏈表的插入操作。如果我們在非第一個結點前進行插入操作,只需要a(i-1)的指標域指向s,然後將s的指標域指向a(i)就行了;如果我們在第一個結點前進行插入操作,頭指標head就要等於新插入結點s
因此,演算法對這兩種情況就要分別設計實現方法。
3、帶頭結點的單鏈表的插入操作:(操作統一,推薦)
上圖中,如果採用帶頭結點的單鏈表結構,演算法實現時,p指向頭結點,改變的是p指標的next指標的值(改變頭結點的指標域),而頭指標head的值不變。
因此,演算法實現方法比較簡單,其操作與對其它結點的操作統一。
問題1:頭結點的好處:
頭結點即在連結串列的首元結點之前附設的一個結點,該結點的資料域中不儲存線性表的資料元素,其作用是為了對連結串列進行操作時,可以對空表、非空表的情況以及對首元結點進行統一處理,程式設計更方便。
問題2:如何表示空表:
無頭結點時,當頭指標的值為空時表示空表;
有頭結點時,當頭結點的指標域為空時表示空表。
如下圖所示:
問題3:頭結點的資料域內裝的是什麼?
頭結點的資料域可以為空,也可存放線性表長度等附加資訊,但此結點不能計入連結串列長度值。
三、單項鍊表的程式碼實現:
1、結點類:
編寫一個Node類來充當結點的模型。我們知道,其中有兩個屬性,1存放資料的data,2存放下一結點的引用,
public class Node {
//為了方便,這兩個變數都使用public,而不用private就不需要編寫get、set方法了。
//存放資料的變數,簡單點,直接為int型
public int data;
//存放結點的變數,預設為null
public Node next;
//構造方法,在構造時就能夠給data賦值
public Node(int data){
this.data = data;
}
}
單鏈表的簡單操作(增加,刪除,獲取總長度,連結串列元素排序,連結串列遍歷)1.1增加結點操作,addNode(Node)
/** * 增加操作 * 直接在連結串列的最後插入新增的結點即可 * 將原本最後一個結點的next指向新結點 */ public void addNode(Node node){ //連結串列中有結點,遍歷到最後一個結點 Node temp = head; //一個移動的指標(把頭結點看做一個指向結點的指標) while(temp.next != null){ //遍歷單鏈表,直到遍歷到最後一個則跳出迴圈。 temp = temp.next; //往後移一個結點,指向下一個結點。 } temp.next = node; //temp為最後一個結點或者是頭結點,將其next指向新結點 }1.2插入結點到連結串列的指定位置。 insertNodeByIndex(int index,Node node)
/** * insertNodeByIndex:在連結串列的指定位置插入結點。 * 插入操作需要知道1個結點即可,當前位置的前一個結點 * index:插入連結串列的位置,從1開始 * node:插入的結點 */ public void insertNodeByIndex(int index,Node node){ //首先需要判斷指定位置是否合法, if(index<1||index>length()+1){ System.out.println("插入位置不合法。"); return; } int length = 1; //記錄我們遍歷到第幾個結點了,也就是記錄位置。 Node temp = head; //可移動的指標 while(head.next != null){//遍歷單鏈表 if(index == length++){ //判斷是否到達指定位置。 //注意,我們的temp代表的是當前位置的前一個結點。 //前一個結點 當前位置 後一個結點 //temp temp.next temp.next.next //插入操作。 node.next = temp.next; temp.next = node; return; } temp = temp.next; } }
1.3刪除指定位置上的結點 delNodeByIndex(int index)
/** * 通過index刪除指定位置的結點,跟指定位置增加結點是一樣的,先找到準確位置。然後進行刪除操作。 * 刪除操作需要知道1個結點即可:和當前位置的前一個結點。 * @param index:連結串列中的位置,從1開始 * */ public void delNodeByIndex(int index){ //判斷index是否合理 if(index<1 || index>length()){ System.out.println("給定的位置不合理"); return; } //步驟跟insertNodeByIndex是一樣的,只是操作不一樣。 int length=1; Node temp = head; while(temp.next != null){ if(index == length++){ //刪除操作。 temp.next = temp.next.next; return; } temp = temp.next; } }
1.4計算單鏈表的長度
/** * 計算單鏈表的長度,也就是有多少個結點 * @return 結點個數 */ public int length() { int length=0; Node temp = head; while(temp.next != null){ length++; temp = temp.next; } return length; }
1.5遍歷單鏈表,列印data
/**
* 遍歷單鏈表,列印所有data
*/
public void print(){
Node temp = head.next;
while(temp != null){
System.out.print(temp.data+",");
temp = temp.next;
}
System.out.println();
}