本文原始碼:GitHub·點這裡 || GitEE·點這裡
一、連結串列簡介
1、連結串列概念
連結串列是一種物理儲存單元上非連續、非順序的儲存結構,資料元素的邏輯順序是通過連結串列中的指標連結次序實現的。連結串列由一系列節點組成,節點可以在執行時動態生成,節點包括兩個部分:一個是儲存資料元素的資料域,另一個是儲存下一個結點地址的指標域。
2、基礎特點
記憶體儲存
邏輯結構
特點描述
- 物理儲存上是無序且不連續的;
- 連結串列是由多個節點以鏈式結構組成;
- 邏輯層面上看形成一個有序的鏈路結構;
連結串列結構解決陣列儲存需要預先知道元素個數的缺陷,可以充分利用記憶體空間,實現靈活的記憶體動態管理。
二、單向連結串列
1、基礎描述
單向連結串列是連結串列的一種,其特點是連結串列的連結方向是單向的,連結串列的遍歷要從頭部開始順序讀取;結點構成,head指標指向第一個成為表頭結點,終止於最後一個指向NULL的指標。
2、基礎操作
新增資料
- 初始化head節點,作為連結串列的頭;
- 修改當前末尾節點的next指標;
- 新新增的節點房子在連結串列末尾;
刪除資料
遍歷找到要刪除的節點,把刪除節點前個節點的指標指向該刪除節點的下個節點;
三、雙向連結串列
1、概念描述
雙向連結串列也叫雙連結串列,是連結串列的一種,連結串列的每個資料結點中都有兩個指標,分別指向直接後繼和直接前驅,從雙向連結串列中的任意一個結點開始,都可以很快速地訪問它的前驅結點和後繼結點,連結串列結構的使用多數都是構造雙向迴圈連結串列。
2、基礎操作
新增資料
- 遍歷找到連結串列的最後一個節點;
- 修改當前末尾節點的next指標;
- 新新增的節點房子在連結串列末尾;
- 新增最新尾節點的prev指標;
刪除資料
- 雙向連結串列,基於要刪除節點操作即可;
- 操作上圖中要刪除的Node2節點;
- Node2.prev.next = Node2.next;
- Node2.next.prev = Node2.prev;
通過上述流程的操作,就把連結串列中一個節點刪除,剩下節點再度連線成鏈式結構。
3、原始碼分析
在Java的API中,LinkedList是典型的雙向連結串列結構,下面基於LinkedList原始碼看雙向連結串列的操作。
基礎案例
public class M01_Linked {
public static void main(String[] args) {
List<User> userList = new LinkedList<>() ;
User removeUser = new User(200,"Second") ;
// 新增元素
userList.add(new User(100,"First")) ;
userList.add(removeUser) ;
userList.add(new User(300,"Third")) ;
System.out.println("初始化:"+userList);
// 修改元素
userList.get(0).setUserName("Zero");
System.out.println("修改後:"+userList);
// 刪除元素
userList.remove(removeUser) ;
System.out.println("刪除後:"+userList);
}
}
class User {
private Integer userId ;
private String userName ;
public User(Integer userId, String userName) {
this.userId = userId;
this.userName = userName;
}
@Override
public String toString() {
return "User{" +
"userId=" + userId +
", userName='" + userName + '\'' +
'}';
}
// 省略Get和Set方法
}
節點描述
節點三個核心描述:資料,next指標,prev指標。
private static class Node<E> {
E item; // 資料
Node<E> next; // 下個指標
Node<E> prev; // 上個指標
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
首位節點處理
基於LinkedList原始碼,首尾節點方式,針對上圖雙連結串列的首位指標特點,這裡原始碼很好理解。
public class LinkedList {
transient Node<E> first;
transient Node<E> last;
// 處理首節點
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
}
// 處理尾節點
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
}
}
新增節點
新增節點的方法直接呼叫linkLast方法,把新節點放到連結串列的尾部即可。
public boolean add(E e) {
linkLast(e);
return true;
}
刪除節點
第一步:遍歷對比,找到要刪除的節點;
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
第二步:移除節點,重新搭建連結串列結構,並且把當前連結串列的資料置為null,並返回被移除的節點;
E unlink(Node<E> x) {
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
return element;
}
如上就是對Java中LinkedList雙連結串列原始碼的部分結構分析,這種程式碼看多了,總感覺自己寫的程式碼不是Java。
四、環形連結串列
在單連結串列中,將終端結點的指標域NULL改為指向表頭結點或開始結點,這樣就形成了環形連結串列:
環形連結串列連結串列的一種結構,特點是表中最後一個結點的指標域指向頭結點,整個連結串列形成一個環。
五、原始碼地址
GitHub·地址
https://github.com/cicadasmile/model-arithmetic-parent
GitEE·地址
https://gitee.com/cicadasmile/model-arithmetic-parent
推薦閱讀:演算法與資料結構
文章標題 |
---|
結構與演算法:稀疏陣列和二維陣列轉換 |
結構與演算法:佇列和棧結構 |
演算法應用:RSA演算法,加密解密,簽名驗籤流程詳解 |
演算法應用:遞迴演算法,處理樹形結構下的業務資料 |