以前咱們分別學習瞭解了動態數組、棧、隊列,其實他們的底層都是依託靜態數組來實現的、只是經過咱們定義的resize
方法來動態擴容解決固定容量的問題,那麼咱們即將學習的鏈表,它實際上是一種真正的動態數據結構。java
鏈表是一種最簡單的動態數據結構,它可以輔助組成其它的數據結構,鏈表中的元素可存儲在內存中的任何地方(不須要連續的內存,這一點和咱們的數組具備很大的區別,數組須要連續的內存),鏈表中的每一個元素都存儲了下一個元素的地址,從而使一系列隨機的內存地址串接在一塊兒
。node
存儲鏈表的數據的咱們通常稱爲節點(Node)
,節點通常分爲兩部分,一部分存儲咱們真正的數據,而另一部分存儲的是下一個節點的引用地址。git
class Node{
private E e; // 存儲的真正元素
private Node next; // 存儲下一個node的引用地址(指向下一個node)
}
複製代碼
好比如今咱們將元素A、B、C三個節點添加到鏈表中,示意圖以下:github
從圖中節點A
到節點B
之間的箭頭表明,節點A
指向了節點B
(NodeA.next = NodeB),由於在實際業務中咱們的鏈表長度不多是無窮無盡的,基本上都是有限個節點,一般定義鏈表中的最後一個元素它的next
存儲的是NULL
(空),換句話說,若是在鏈表中,一個節點的next
是空(NULL
)的話,那麼它必定是最後一個節點(對應咱們圖中的C
節點)。數組
根據咱們上面介紹的鏈表基本結構,下面咱們用代碼定義一下鏈表的基本骨架(這裏咱們引入了一個虛擬的頭結點
,下面咱們會做說明)數據結構
public class LinkedList<E> {
/** * 虛擬的頭結點 */
private Node dummyHead;
/** * 鏈表中節點的個數 */
private int size;
public LinkedList() {
// 建立一個虛擬的頭結點
dummyHead = new Node();
}
// 節點定義
private class Node {
// 存儲節點的元素
public E e;
// 存儲下一個節點的地址
public Node next;
public Node() {
}
public Node(E e) {
this.e = e;
}
public Node(E e, Node next) {
this.e = e;
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"e=" + e +
", next=" + next +
'}';
}
}
}
複製代碼
後面咱們對鏈表的添加節點、刪除節點以及查詢節點,代碼實現都會基於這個基本骨架ide
通常咱們向鏈表中添加節點,基本思路:找到添加節點位置的前一個節點preNode
,而後再改變鏈表的地址引用;因爲鏈表的第一個節點也就是頭結點沒有前節點,此時咱們爲了操做方便,爲鏈表新增了不存儲任何元素的一個虛擬的頭結點dummyHead
(不是必須的,對用戶來說是不可見的),其實鏈表中真正的第一個節點是節點A
(dummyHead.next),這樣咱們就能保證了,鏈表中存儲元素的節點都有前一個節點。學習
下面咱們來看一下,若是如今有一個節點D
,咱們準備把它插入到節點B
的位置,咱們須要作哪些操做this
第一步:咱們首先要找到節點B
的前一個節點,也就是節點A
spa
第二步:將新節點D
的指向指到節點B
(NodeD.next = NodeA.next
),而後再將節點A
的指向,指到節點D
(NodeA.next = NodeD
),這樣咱們的節點就能串接起來了
/** * 向鏈表中指定位置插入節點(學習使用,真正插入不會指定索引) * * @param index 索引的位置 * @param e 節點元素 */
public void add(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("不是有效的索引");
}
Node prev = dummyHead;
// 找到index位置的前一個節點
for (int i = 0; i < index; i++) {
prev = prev.next;
}
// 新建一個節點,進行掛接
Node node = new Node(e);
node.next = prev.next;
prev.next = node;
size++;
}
複製代碼
進行鏈表遍歷,咱們須要從鏈表中真正的第一個元素開始,也就是dummyHead.next
/** * 獲取鏈表中index位置的元素 * * @param index 索引的位置 * @return 節點的元素 */
public E get(int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("不是有效的索引");
}
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
return cur.e;
}
複製代碼
/** * 修改鏈表中index位置節點的元素 * * @param index 索引的位置 * @param e 節點的元素 */
public void set(int index, E e) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("不是有效的索引");
}
Node cur = dummyHead.next;
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.e = e;
}
複製代碼
/** * 查找鏈表中是否包含元素e * * @param e * @return */
public boolean contains(E e) {
Node cur = dummyHead.next;
while (cur != null) {
if (cur.e.equals(e)) {
return true;
}
cur = cur.next;
}
return false;
}
複製代碼
在鏈表中刪除元素,與在鏈表中添加元素有點相似
第一步:咱們首先找到刪除節點位置的前一個節點,咱們用prev
表示,被刪除的節點咱們用delNode
表示
第二步:改變鏈表的引用地址:prev.next = delNode.next
(等同於,將節點在鏈表中刪除)
/** * 刪除鏈表中index位置的節點 * * @param index */
public void remove(int index) {
if (index < 0 || index > size) {
throw new IllegalArgumentException("不是有效的索引");
}
Node prev = dummyHead;
for (int i = 0; i < index; i++) {
prev = prev.next;
}
Node delNode = prev.next;
prev.next = delNode.next;
delNode.next = null;
size--;
}
複製代碼
完整版代碼GitHub倉庫地址:Java版數據結構-鏈表 歡迎你們【關注】和【Star】
至此筆者已經爲你們帶來了數據結構:靜態數組、動態數組、棧、數組隊列、循環隊列、鏈表;接下來,筆者還會一一的實現其它常見的數組結構,你們一塊兒加油!
持續更新中,歡迎你們關注公衆號:小白程序之路(whiteontheroad),第一時間獲取最新信息!!!