一、連結串列簡介
連結串列是有序的列表,在記憶體中的儲存方式如圖:
- 連結串列是以節點的方式來儲存,是鏈式儲存;
- 每個節點包含data域,next指標(指向下一個節點);
- 連結串列的節點不一定連續儲存,是離散的狀態;
- 連結串列分為帶頭節點的連結串列和沒有頭結點的連結串列,帶頭結點的邏輯結構示意圖如下:
二、單連結串列的應用例項
2.1 單連結串列的基礎方法的實現
使用帶頭結點的單向連結串列實現,完成對水滸英雄的CRUD。
- 普通新增方法
此方法新增英雄時,直接新增到連結串列尾部。
- 先建立一個head節點,不記錄具體資訊,作用就是表示單連結串列的頭結點;
- 此後每新增一個節點,就直接新增在連結串列的最後
- 示意圖:
- 按序新增方法
此方法在新增英雄時,根據英雄的排名將英雄新增到指定的位置。
- 首先找到新節點應該新增的位置,通過輔助指標來遍歷找到位置;
- 將新節點的next設定為指標指向節點的next節點;
- 將指標指向的節點的next設定新節點;(2、3步驟順序不能互換)
- 不能新增相同排名的英雄;
- 示意圖:
- 修改節點方法
- 先通過遍歷找到對應節點;
- 修改資料。
- 刪除節點方法
- 找到需要刪除的del節點;
- 將del節點的前一個節點的next指向del節點的next節點;
- 示意圖:
- 程式碼實現
GitHub:https://github.com/goSilver/daydayup/blob/master/datastructure/src/hanshunping/chapter04/SingleLinkedListDemo.java
2.2 單連結串列的相關面試題
- 求單連結串列中的有效節點的個數
思路:
遍歷單連結串列,如果有頭結點忽略頭結點
程式碼:
/**
* 獲取連結串列的長度
*
* @param head 連結串列頭結點
* @return 長度
*/
int getLength(HeroNode head) {
int length = 0;
while (Objects.nonNull(head.getNext())) {
length++;
head = head.getNext();
}
return length;
}
- 查詢單連結串列中的倒數第k個節點
思路:
- 先求得連結串列的長度length;
- 用length - k即可算出倒數第k個節點所處位置;
- 從連結串列頭結點遍歷length - k次即可。
程式碼:
/**
* 尋找倒數第k個節點
*
* @param singleLinkedList 連結串列
* @param k k值
* @return 倒數第k個節點
*/
HeroNode findTheKthFromBottom(SingleLinkedList singleLinkedList, int k) {
int length = getLength(singleLinkedList.head);
int index = length + 1 - k;
HeroNode temp = singleLinkedList.head;
while (Objects.nonNull(singleLinkedList.head.getNext()) && index > 0) {
temp = temp.getNext();
index--;
}
return temp;
}
- 反轉連結串列
- 定義一個新的頭結點newHead;
- 遍歷原來的連結串列,每遍歷一個節點,就將這個節點放到newHead的next即可;
- 列印時以newHead為頭結點列印。
示意圖:
程式碼:
/**
* 反轉連結串列
* 思路:
* 定義一個新的頭結點newHead,遍歷連結串列,依次將遍歷的節點取出放到newHead的後面即可
*
* @param oldHead 連結串列的舊頭結點
*/
void reverse(HeroNode oldHead) {
// 定義一個新的頭結點
HeroNode newHead = new HeroNode(0L, "", "");
// 定義一個臨時節點,記錄即將遍歷的下一個節點
HeroNode next;
// 跳過頭節點
HeroNode current = oldHead.getNext();
while (Objects.nonNull(current)) {
// 記錄即將遍歷的下一個節點
next = current.getNext();
// 將newHead的下一個節點設定為當前節點的next
current.setNext(newHead.getNext());
// 將newHead的next設定為當前節點
newHead.setNext(current);
// 恢復遍歷指標
current = next;
}
// 修改連結串列頭結點
head = newHead;
}
- 從尾到頭列印單連結串列
思路:
方式1:將單連結串列先進行反轉操作,然後再遍歷列印即可,缺點是會破壞原來的單連結串列的儲存結構;
方式2:可以利用棧資料結構先進後出的特性,從頭到尾遍歷時將節點依次壓入棧中,列印時依次從棧中pop即可。
程式碼:
/**
* 逆向列印連結串列
* 思路:
* 1、先反轉連結串列,再列印連結串列,缺點是會破壞原連結串列的結構
* 2、利用棧資料結構先進後出的特性來輔助實現
* 這裡用棧來實現
*/
void reversePrint() {
Stack<HeroNode> nodeStack = new Stack<>();
HeroNode current = head.getNext();
while (Objects.nonNull(current)) {
nodeStack.push(current);
current = current.getNext();
}
while (!nodeStack.isEmpty()) {
System.out.println(nodeStack.pop());
}
}