JAVA實現單鏈表的增刪改查
阿新 • • 發佈:2019-01-30
前言
實現參考:
建議閱讀以上部落格,我的部落格更偏向於[我]能看懂
上面的部落格則偏向於[大家]都能看懂
實現的功能
增加
- 指定位置插入節點
- 在末尾增加節點
刪除
- 刪除指定節點,通過節點資料(假設資料不重複)
- 刪除指定位置節點
修改
- 修改指定位置節點資料
查詢
- 查詢某個節點的位置
- 查詢某個位置中的節點資料
- 查詢某個資料是否存在於連結串列中(假設資料不重複)
轉陣列
- 將連結串列轉成陣列
中文版虛擬碼
/**
*
* 單向連結串列
* 節點:
* 資料都存放在一個節點中
* 每個節點都有指向下一個節點的指標
* 頭指標:
* 連結串列中的頭個節點,不計入節點數,表示連結串列頭
* 當前指標:
* 在連結串列中游走,指向哪,跑到哪。
* 增加
* 新增節點到尾部時
* 空連結串列:新增的節點成為頭指標的next指向的節點
* 非空連結串列:成為最後一個節點的下一個節點,更新當前指標為下一個節點
* 新增節點到第n個位置時
* 如果插入的位置不是頭:
* 到達第n-1個節點
* 新節點指向第n-1個的next節點
* 第n-1個節點的next指向新節點
* 如果是第一個位置(頭):
* 新節點的next指向頭指標的節點
* 更新頭指標的next指向新節點
* 刪除
* 根據資料刪除
* 判斷資料是否存在或者是否為空
* 如果存在,且位置為n
* 將當前指標指向第n-1個節點
* 更新第n-1個節點的next指向第n+1個節點
* 否則
* 丟擲異常
* 根據傳入的位置n刪除
* 判斷引數n是否合法 (n >= 0 && n < size)
* 若合法
* 將當前指標指向第n-1個節點
* 更新第n-1個節點的next指向第n+1個節點
* 否則
* 丟擲異常
*
* 查詢
* 根據資料查詢節點位置
* 判斷輸入資料的合法性
* 合法
* 遍歷查詢
* 找到,返回位置
* 沒找到,返回-1
* 獲取準確資料,根據傳入位置n查詢
* 判斷輸入資料的合法性
* 定位到n,返回資料
* 存在性查詢,根據傳入資料查詢連結串列中是否存在這樣一個節點
* 判斷輸入資料的合法性
* 遍歷查詢
* 存在,返回真
* 不存在,返回假
* 修改
* 傳入資料和位置
* 驗證位置的合法性
* 合法
* 當前指標跳轉到目標節點修改
* 返回真
* 不合法
* 返回假
*/
具體實現
public class SingleLinkedList<T> {
private Node headNode;
private Node currNode;
private int size;
SingleLinkedList() {
headNode = new Node();
currNode = null;
size = 0;
}
public int Size() {
return size;
}
//新增節點到連結串列尾部
public void insert(T data) {
Node tNode = new Node(data);
if (isEmpty()) {
headNode.setNext(tNode);
} else {
toIndexOf(size - 1);
currNode.setNext(tNode);
}
size++;
}
//新增節點到第n個位置
public void insert(T data, int n) {
//將當前指標定位到第n-1個節點
toIndexOf(n - 1);
//如果為空,插入到第一個節點
if (isEmpty()) {
insert(data);
return;
}
// 將新節點的next指向第n個節點, 第n-1個節點的next指向新節點
// 即新節點為第n個,原第n個節點為n+1
currNode.setNext(new Node(data, currNode.getNext()));
size++;
}
//查詢第n個節點的資料
public <T> T get(int n) {
//當前指標移動到第n個節點
toIndexOf(n);
return (T) currNode.getData();
}
//查詢某節點的位置
public int getElemAt(T data) {
if (data == null) {
throw new NullPointerException("既然喜歡null,那就給你咯。");
}
currNode = headNode.getNext();
int i = 0;
while (currNode != null) {
if (currNode.getData().equals(data)) {
return i;
}
i++;
currNode = currNode.getNext();
}
return -1;
}
//修改節點的資料
public boolean setElemAt(T data, int n) {
toIndexOf(n);
currNode.setData(data);
return true;
}
//查詢某節點是否存在
public boolean contains(T data) {
return getElemAt(data) == -1 ? false : true;
}
//刪除第n個節點
public T delete(int n) {
toIndexOf(n - 1);
T data = currNode.getNext().getData();
currNode.setNext(currNode.getNext().getNext());
size--;
return data;
}
//刪除指定節點
public T deleteElemBy(T data) {
int n = getElemAt(data);
return delete(n);
}
public boolean isEmpty() {
return size == 0 ? true : false;
}
//將其轉為陣列
public Object[] toArray() {
if (isEmpty()) {
throw new IndexOutOfBoundsException("Size: " + size);
}
Object[] arr = new Object[size];
toIndexOf(0);
int i = 0;
while (currNode != null) {
arr[i++] = currNode.getData();
currNode = currNode.getNext();
}
return arr;
}
//將當前指標移動到指定位置
private void toIndexOf(int n) {
if (isEmpty() || !isIndexOk(n)) {
throw new IndexOutOfBoundsException("Index: " + n + ", Size: " + size);
}
currNode = headNode;
if (n == -1) return;
while (n != -1) {
currNode = currNode.getNext();
n--;
}
}
private boolean isIndexOk(int n) {
if (n < -1 || n >= size) return false;
return true;
}
private class Node {
private T data;
private Node next;
Node() {
}
Node(T data) {
this.data = data;
}
Node(T data, Node nextNode) {
this.data = data;
this.next = nextNode;
}
public T getData() {
return data;
}
public Node getNext() {
return next;
}
public void setData(T data) {
this.data = data;
}
public void setNext(Node next) {
this.next = next;
}
}
}
測試
public class LinkedListTest {
public static void main(String[] args) {
SingleLinkedList<Integer> test = new SingleLinkedList<Integer>();
for (int i = 0; i < 10; i++) {
test.insert(i);
}
//System.out.println("------------越界測試--------------");
//System.out.println(test.get(-1));
//System.out.println(test.get(test.Size()));
System.out.println("角標讀取:");
System.out.print(test.get(0) + ",");
System.out.print(test.get(4) + ",");
System.out.println(test.get(test.Size() - 1));
System.out.println("------------轉換陣列--------------");
Object[] data = test.toArray();
showArray(data);
System.out.println("------------根據元素獲取角標--------------");
int el1 = 2, el2 = 22;
System.out.println(el1 + " at " + test.getElemAt(2));
System.out.println(el2 + " at " + test.getElemAt(22));
System.out.println("------------原陣列--------------");
data = test.toArray();
showArray(data);
System.out.println("角標刪除測試,刪除");
System.out.print(test.delete(9) + ",");
System.out.print(test.delete(0) + ",");
System.out.println(test.delete(3));
System.out.println("刪除完畢");
data = test.toArray();
showArray(data);
System.out.println("元素刪除測試,刪除:");
System.out.print(test.deleteElemBy(1) + ",");
System.out.print(test.deleteElemBy(8) + ",");
System.out.println(test.deleteElemBy(5));
System.out.println("刪除完畢:");
data = test.toArray();
showArray(data);
System.out.println("------------包含測試--------------");
data = test.toArray();
showArray(data);
int n1 = 6, n2 = 10;
System.out.println(n1 + ",在嗎?" + test.contains(n1));
System.out.println(n2 + ",在嗎?" + test.contains(n2));
System.out.println("------------插入測試--------------");
data = test.toArray();
showArray(data);
test.insert(4, 2);
test.insert(5, 3);
System.out.println("插入4、5分別到第2、3個位置,完成:");
data = test.toArray();
showArray(data);
System.out.println("------------修改測試--------------");
data = test.toArray();
showArray(data);
test.setElemAt(1024, 2);
test.insert(2048, 3);
System.out.println("修改角標2、3分別到為1024、2048,完成:");
data = test.toArray();
showArray(data);
test.setElemAt(6, 10);
}
public static void showArray(Object[] data) {
int len = data.length - 1;
for (int i = 0; i <= len; i++) {
System.out.print(data[i]);
System.out.print(i < len ? " " : "");
}
System.out.println();
}
}
結果
角標讀取:
0,4,9
------------轉換陣列--------------
0 1 2 3 4 5 6 7 8 9
------------根據元素獲取角標--------------
2 at 2
22 at -1
------------原陣列--------------
0 1 2 3 4 5 6 7 8 9
角標刪除測試,刪除
9,0,4
刪除完畢
1 2 3 5 6 7 8
元素刪除測試,刪除:
1,8,5
刪除完畢:
2 3 6 7
------------包含測試--------------
2 3 6 7
6,在嗎?true
10,在嗎?false
------------插入測試--------------
2 3 6 7
插入4、5分別到第2、3個位置,完成:
2 3 4 5 6 7
------------修改測試--------------
2 3 4 5 6 7
修改角標2、3分別到為1024、2048,完成:
2 3 1024 2048 5 6 7
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 10, Size: 7
at SingleLinkedList.toIndexOf(SingleLinkedList.java:183)
at SingleLinkedList.setElemAt(SingleLinkedList.java:135)
at LinkedListTest.main(LinkedListTest.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
收穫
第一次用泛型(大概是亂用)
靜態內部類和非靜態內部類的區別
靜態內部類不能訪問外部類的非靜態成員
類似於把靜態內部類獨立了
意識到連結串列的各種操作十分耗時!!查詢一個,查一個就要迴圈遍歷一遍也是沒誰了。
腦海裡多了個概念,一個類似遊標指標的概念,指定它去哪個位置就去哪個位置獲取元素,不知道叫遊標合適不合適,總之遊標很忙。在我的實現裡的currNode就是經常跑頭跑尾。
把連結串列操作完轉成陣列會提高效率,也許?
發現自己對異常不熟悉到大腦一片空白