算法和數據結構是程序員逃不過的一個坎,因此趁着閒餘時間,開始學習基礎的算法和數據結構。這裏記錄下本身實現簡單的單項鍊表的過程,若有錯誤,敬請指正。node
在Java中,經常使用的數據容器裏面,跟鏈表關係緊密的當屬LinkedList了,它的底層實現爲雙向鏈表,這裏就以它爲參照物,實現本身的簡單的單向鏈表。另外,還須要支持增刪改查、獲取大小等功能。 以下所示,先定義了增刪改查方法支持的範圍。git
/**
* 增刪改查方法
* add(index, E) index >= 0 && index <= size
* remove(index) index >= 0 && index < size
* set(index, E) index >= 0 && index < size
* get(index) index >= 0 && index < size
*/
複製代碼
結點的定義很簡單,這裏爲了簡單起見,不支持泛型,結點存儲的數據類型固定爲int。程序員
private static class Node {
private int item;
private Node next;
public Node(int item, Node next) {
this.item = item;
this.next = next;
}
public void setItem(int item) {
this.item = item;
}
@Override
public String toString() {
return String.valueOf(this.item);
}
}
複製代碼
既然是單向鏈表,容器中只須要持有first引用便可,不須要last引用。再須要定義一個size用來表示鏈表大小;另外,爲了方便查看測試結果,咱們重寫toString方法。代碼以下:github
public class SingleLinkedList {
private int size = 0;
private Node first = null;
...
}
複製代碼
/**
* 返回當前容器大小。
* @return
*/
public int getSize() {
return size;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
if (first != null) {
Node curr = first;
while (curr != null) {
sb.append(String.valueOf(curr));
if (curr.next != null) {
sb.append(",").append(" ");
}
curr = curr.next;
}
}
sb.append("]");
return sb.toString();
}
複製代碼
在已有數據中獲取數據,這個最爲簡單,因爲單向鏈表容器中只存儲首個結點first引用,因此不能直接角標index獲取,須要從first中根據角標依次遍歷,代碼以下:算法
/**
* 獲取角標上的數據。
*
* @param index
* @return
*/
public Node get(int index) {
checkElementIndex(index);
Node x = first;
for (int i = 0; i < index; i++) {
x = x.next;
}
return x;
}
複製代碼
與另外三組方法(獲取、修改、刪除)均不一樣,增長方法不單單要支持鏈表現有的數據範圍,並且還須要支持角標爲size的位置。 在當前位置插入結點,須要獲取到前一個結點,讓前一個結點的next指向新增的結點,再讓新增結點的next指向該位置原來的結點。須要注意一些特殊狀況的處理,還有不要忘了size的數值增長。代碼以下:bash
/**
* 在角標爲index的位置插入數據
*
* @param index
* @param value
* @return
*/
public boolean add(int index, int value) {
checkPositionIndex(index);
if (index == 0) {
if (first == null) {
first = new Node(value, null);
} else {
Node next = get(0);
Node node = new Node(value, next);
first = node;
}
} else {
Node prev = get(index - 1);
Node node = new Node(value, prev.next);
prev.next = node;
}
size++;
return true;
}
複製代碼
修改方法也很簡單,只需根據角標獲取已經存在結點,而後將結點中存儲的數據修改便可,代碼以下:數據結構
public boolean set(int index, int value) {
checkElementIndex(index);
Node node = get(index);
node.setItem(value);
return true;
}
複製代碼
刪除當前位置的結點,核心是讓上一個結點的next指向當前位置的下一個結點便可,與添加方法相似,須要作好極端狀況的處理,代碼以下:app
public boolean remove(int index) {
checkElementIndex(index);
// size > 0
if (getSize() == 1) {//size == 1
first = null;
} else {// size > 1
if (index == 0) {// 刪除第一個數據
first = first.next;
} else if (getSize() - 1 == index) {// 刪除最後一個數據
Node prev = get(index - 1);
prev.next = null;
} else {// 刪除中間的數據
get(index - 1).next = get(index).next;
}
}
size--;
return true;
}
複製代碼
爲了實現單向鏈表的反轉,在遍歷的過程當中,讓每一個節點的next指向上一個結點。代碼以下:ide
/**
* 反轉鏈表
*
* @return
*/
public Node reverseList() {
Node prev = null;
Node curr = first;
while (curr != null) {
Node temp = curr.next;
curr.next = prev;
prev = curr;
curr = temp;
}
logFromHead("reverseList", prev);
return prev;
}
複製代碼
項目中搜索SingleLinkedList便可。 github傳送門 github.com/tinyvampire…學習
gitee傳送門 gitee.com/tinytongton…