鏈表可能是繼數組之後第二種使用最爲廣泛的數據結構。
鏈表有單鏈表、雙端鏈表、有序鏈表、雙向鏈表。本文講的就是鏈表中的基本即單鏈表,來了解鏈表的基本操作和概念。
通過下圖簡單瞭解鏈表的概念:
其特點舉個簡單例子,如圖把33這個元素插入在42之前,數組實現的話要把42開始之後爲元素全部後移一位,然後把33插到指定位置,然而鏈表則可以隨便的把他分配在某一個位置,然後我們通過修改指針關係,把散落在各個位置的單元串聯起來,把原來指向42的指針指向33,並把33的下一個指針指向42僅僅這2步動作就完成了把33這個新單元加入到我們的鏈表中,並且後面這些元素7 98等根本沒有感受到這個插入動作,對他們沒有任何影響。
這樣內存的分配比較靈活,可以隨意的分配。
刪除的時候同理。要把33這個單元刪除,只要把指向它的那個指針再指向42就可以了,其他的什麼都不需要動,注意此時的33單元沒有任何指針指向它,現在它變成了垃圾對象,將來會被系統回收。然而單鏈表的隨機訪問很慢,要從head找到第一個元素然後找找找。。
還有兩個概念即鏈結點和鏈表類;
上圖中的每一項都是鏈結點,一個鏈結點是某個類的對象,這個類可以叫做Link。因爲一個鏈表中有許多類似的鏈結點,所以有必要用一個不同於鏈表的類來表達鏈結點。每個Link對象中都包含一個對下一個鏈結點引用的字段(通常叫做next),但是鏈表本身的對象中有一個字段指向對第一個鏈結點的引用。
鏈表:LinkList類中只包含一個數據項:即對鏈表中第一個鏈結點的引用,叫做head。他是唯一的鏈表需要維護的永久信息,用以定位所有其他的鏈結點。從head出發,沿着鏈表通過每個鏈結點的next字段,就可以找到其他的鏈結點。
實現的代碼如下:
public class YezhuLinkedList {
public Link head = null;
public boolean isEmpty() {
return head == null;
}
/**
* insertFirst() 作用是在表頭插入一個新鏈結點。實際上,這是最容
* 易插入的一個鏈結點。具體操作時,新創建的鏈結點的next指向原來
* head指向的值,然後head指向新插入的鏈結點。
*/
public void insertFirst(int a) {
Link link = new Link(a);
link.next = head; //新插入的元素 指向原來head指向的鏈接點
head = link;//head指向新插入的元素
}
/**
* 展示全部的結點數據
*/
public void showAll() {
//從head開始遍歷 到最後一個元素的時候 指向的肯定是空
Link current = head;
while (current != null) {
System.out.println(current.data);
current = current.next;
}
}
/**
* 在尾部插入元素
* 從頭開始一直遍歷 遍歷到尾部 讓之前Link的next指向新的結點
*/
public void insertLast(int data) {
Link link = new Link(data);
if (head == null) {
link.next = head;
head = link;
} else {
Link current = head;
while (current.next != null) {
current = current.next;
}
current.next = link;
}
}
/**
* 刪除頭元素
*/
public void deleteFirst() {
Link current = head;
head = current.next;
}
/**
* 長度
*
* @return
*/
public int length() {
int length = 0;
Link link = head;
while (link != null) {
length++;
link = link.next;
}
return length;
}
/**
* 根據元素的關鍵字 找到其結點所在的位置
*
* @param data
* @return
*/
public int getPosByValue(int data) {
Link current = head;
int position = 0;
if (!contains(data)) {
return -1;
} else {
while (current.data != data) {
position++;
if (current.next == null) {
return position;
} else {
current = current.next;
}
}
return position;
}
}
/**
* 整個鏈表中是否包含關鍵字爲data的鏈結點
*
* @param data
* @return
*/
public boolean contains(int data) {
Link current = head;
int position = 0;
while (current.data != data && current.next != null) {
//判斷條件加上current.next != null 是爲了隊尾的時候的空指針
position++;
current = current.next;
}
//遍歷到鏈表的尾部 要是position到了尾部 說明沒有包含此元素
if (position == length() - 1) {
return false;
} else {
return true;
}
}
}
測試代碼:
/**
* 測試
*/
public class Test {
public static void main(String[] args) {
YezhuLinkedList yezhuLinkedList = new YezhuLinkedList();
yezhuLinkedList.insertFirst(1);
yezhuLinkedList.insertFirst(5);
yezhuLinkedList.insertLast(8);
yezhuLinkedList.insertFirst(3);
yezhuLinkedList.showAll();
//測試是否包含關鍵字是data的鏈結點,有則輸出位置
int data = 10;
if (yezhuLinkedList.contains(data)) {
System.out.println("包含此元素,在鏈表中的位置是:" + yezhuLinkedList.getPosByValue(data));
} else {
System.out.println("不包含此元素");
}
// yezhuLinkedList.deleteFirst();
System.out.println("鏈表長度是" + yezhuLinkedList.length());
}
}
輸出結果:
其過程中我們可以看到一個這樣的結構,能更清楚的瞭解鏈表的屬性結構:
顯然就是從head鏈結點一個指向一個的展開。
如上代碼如有問題請多多指出。
關於鏈結點補充:
1.鏈結點中的數據域可能包含很多的數據項,比如一個個人記錄可能有姓名、地址、社保號、頭銜等許多字段。通常用一個包含這些數據的類的對象來代替這些數據項。
2.可以讓每個鏈表的鏈結點是一個小型的數組,比如我們一個數組要表達十萬個這樣的數據,我們可以把每一百或一千個這樣的數組做成一個小單元,把這樣的小單元用鏈表串
聯起來,那麼它既有數組的結構又有鏈表的優點,也就是數據結構靈活的綜合的應用。
也就是說真實的鏈表在實際使用過程中有很多的變種,一定要靈活的選擇合適的機構。