使用Java實現單向連結串列,並完成連結串列反轉。
使用Java實現單向連結串列,並完成連結串列反轉。
演算法和資料結構是程式設計師逃不過的一個坎,所以趁著閒餘時間,開始學習基礎的演算法和資料結構。這裡記錄下自己實現簡單的單項鍊表的過程,如有錯誤,敬請指正。
明確需求
在Java中,常用的資料容器裡面,跟連結串列關係緊密的當屬LinkedList了,它的底層實現為雙向連結串列,這裡就以它為參照物,實現自己的簡單的單向連結串列。另外,還需要支援增刪改查、獲取大小等功能。
如下所示,先定義了增刪改查方法支援的範圍。
/** * 增刪改查方法 * 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 */
定義連結串列結點Node
結點的定義很簡單,這裡為了簡單起見,不支援泛型,結點儲存的資料型別固定為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方法。程式碼如下:
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(); }
定義查詢方法get(index)
在已有資料中獲取資料,這個最為簡單,由於單向連結串列容器中只儲存首個結點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;
}
定義增加方法add(index, value)
與另外三組方法(獲取、修改、刪除)均不同,增加方法不僅僅要支援連結串列現有的資料範圍,而且還需要支援角標為size的位置。
在當前位置插入結點,需要獲取到前一個結點,讓前一個結點的next指向新增的結點,再讓新增結點的next指向該位置原來的結點。需要注意一些特殊情況的處理,還有不要忘了size的數值增加。程式碼如下:
/**
* 在角標為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;
}
定義修改方法set(index, value)
修改方法也很簡單,只需根據角標獲取已經存在結點,然後將結點中儲存的資料修改即可,程式碼如下:
public boolean set(int index, int value) {
checkElementIndex(index);
Node node = get(index);
node.setItem(value);
return true;
}
定義刪除方法remove(index)
刪除當前位置的結點,核心是讓上一個結點的next指向當前位置的下一個結點即可,與新增方法類似,需要做好極端情況的處理,程式碼如下:
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指向上一個結點。程式碼如下:
/**
* 反轉連結串列
*
* @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傳送門 https://github.com/tinyvampirepudge/DataStructureDemo
gitee傳送門 https://gitee.com/tinytongtong/DataStructureDemo
參考
https://leetcode.com/problems/reverse-linked-list/description/