帶頭節點單鏈表ide
1.優點:post
1)當鏈表爲空時,指針指向頭結點,不會發生null指針異常ui
2)方便特殊操做(刪除第一個有效節點或者插入一個節點在表頭)this
3)單鏈表加上頭結點以後,不管單鏈表是否爲空,頭指針始終指向頭結點,所以空表和非空表的處理也統一了,方便了單鏈表的操做,也減小了程序的複雜性和出現bug的機會。spa
4)代碼更加簡單易懂指針
2.相關操做code
1)創建單鏈表 即創建一個頭結點blog
static class Entry<T> { T data; // 鏈表節點的數據域 Entry<T> next; // 下一個節點的引用 public Entry(T data, Entry<T> next) { this.data = data; this.next = next; } } /** * 頭節點的類型 * * @param <T> */ private static class HeadEntry<T> extends Entry<T> { int cnt; // 用來記錄節點的個數 public HeadEntry(int cnt, T data, Entry<T> next) { super(data, next); this.cnt = cnt; } }
2)頭插:新建節點next域指向頭結點的next域,再將頭結點next指向新節點。rem
public void insertHead(T val) { Entry<T> newNode = new Entry<>(val, null); newNode.next = this.head.next; this.head.next = newNode; }
3)尾插:從頭結點開始遍歷,當next出現null時,已經到達尾部,將next指向新節點便可get
public void insertTail(T val) { Entry<T> temp = this.head; Entry<T> newNode = new Entry<>(val, null); while (temp.next != null) { temp = temp.next; } temp.next = newNode; }
4)刪除值爲val的節點 :設置一前一後指針進行遍歷,當後指針發現值爲val的節點時,前指針的next域指向後指針的next域便可
public void remove(T val) { Entry<T> frontTemp = this.head; Entry<T> temp = frontTemp.next; while (temp.data != val) { frontTemp = frontTemp.next; temp = temp.next; } frontTemp.next = temp.next; }
5)逆置單鏈表 :將第二個有效節點不斷進行頭插操做,就會產生逆置效果
將鏈表從第二個有效節點開始一分爲二,不斷頭插進前一鏈表
public void reverse() { if (this.head.next == null) { return; } Entry<T> cur = this.head.next.next; this.head.next.next = null; Entry<T> post = null; while (cur != null) { post = cur.next; cur.next = head.next; head.next = cur; cur = post; } }
6)獲取倒數第k個節點的值:設置兩個相距K的指針,當前指針到達鏈表尾部時,前指針指向倒數第k個節點
public T getLastK(int k) { Entry<T> cur1 = this.head.next; Entry<T> cur2 = cur1.next; for (int i = 1; i < k; i++) { cur2 = cur2.next; if (cur2 == null) { return null; } } while (cur2 != null) { cur1 = cur1.next; cur2 = cur2.next; } return cur1.data; }
7)判斷鏈表是否有環,若是有,返回入環節點的值;不然返回null:
設置快慢指針,快指針一次移動兩次,慢指針一次移動一次,若是出現快指針指向的節點等於慢指針指向的節點 ,即證實節點有環。
入環節點的求法:
設相遇點爲A點;當快指針通過 x+2y+z個節點時時 慢指針走了x+y個節點 可得2(x+y)=x+2y+z 化簡可得x=z;因此
fast從第一個節點開始走,slow從快慢指針相交的地方開始走,它們相遇的時候,就是環的入口節點
public T isLinkCircle() { //利用快慢指針檢測是否有環 Entry<T> slow = this.head.next; Entry<T> quick = this.head.next; while (quick != null && quick.next != null) { slow = slow.next; quick = quick.next.next; if (slow == quick) { break; } } if (quick == null) { return null; } else { // fast從第一個節點開始走,slow從快慢指針相交的地方開始走,它們相遇的時候,就是環的入口節點 quick = this.head.next; while (quick != slow) { quick = quick.next; slow = slow.next; } return slow.data; } }
8)合併兩個有序單鏈表
public void merge(Link<T> link) { Entry<T> p = this.head; Entry<T> p1 = this.head.next; Entry<T> p2 = link.head.next; // 比較p1和p2節點的值,把值小的節點掛在p的後面 while (p1 != null && p2 != null) { if (p1.data.compareTo(p2.data) >= 0) { p.next = p2; p2 = p2.next; } else { p.next = p1; p1 = p1.next; } p = p.next; } if (p1 != null) { // 鏈表1還有剩餘節點 p.next = p1; } if (p2 != null) { // 鏈表2還有剩餘節點 p.next = p2; } }
總源代碼
class Link<T extends Comparable<T>> { // 指向單鏈表的第一個節點 Entry<T> head; public Link() { this.head = new Entry<>(null, null); } /** * 單鏈表的頭插法 * * @param val */ public void insertHead(T val) { Entry<T> newNode = new Entry<>(val, null); newNode.next = this.head.next; this.head.next = newNode; } /** * 單鏈表的尾插法 * * @param val */ public void insertTail(T val) { Entry<T> temp = this.head; Entry<T> newNode = new Entry<>(val, null); while (temp.next != null) { temp = temp.next; } temp.next = newNode; } /** * 單鏈表中刪除值爲val的節點 * * @param val */ public void remove(T val) { Entry<T> frontTemp = this.head; Entry<T> temp = frontTemp.next; while (temp.data != val) { frontTemp = frontTemp.next; temp = temp.next; } frontTemp.next = temp.next; } /** * 逆置單鏈表 */ public void reverse() { if (this.head.next == null) { return; } Entry<T> cur = this.head.next.next; this.head.next.next = null; Entry<T> post = null; while (cur != null) { post = cur.next; cur.next = head.next; head.next = cur; cur = post; } } /** * 獲取倒數第K個單鏈表節點的值 * 1.遍歷一次鏈表,統計鏈表節點的個數length * 2.length-k * 只遍歷一次鏈表,找出數 * * @param k */ public T getLastK(int k) { Entry<T> cur1 = this.head.next; Entry<T> cur2 = cur1.next; for (int i = 1; i < k; i++) { cur2 = cur2.next; if (cur2 == null) { return null; } } while (cur2 != null) { cur1 = cur1.next; cur2 = cur2.next; } return cur1.data; } /** * 判斷鏈表是否有環,若是有,返回入環節點的值;不然返回null * * @return */ public T isLinkCircle() { //利用快慢指針檢測是否有環 Entry<T> slow = this.head.next; Entry<T> quick = this.head.next; while (quick != null && quick.next != null) { slow = slow.next; quick = quick.next.next; if (slow == quick) { break; } } if (quick == null) { return null; } else { // fast從第一個節點開始走,slow從快慢指針相交的地方開始走,它們相遇的時候,就是環的入口節點 quick = this.head.next; while (quick != slow) { quick = quick.next; slow = slow.next; } return slow.data; } } /** * 打印單鏈表的全部元素的值 */ public void show() { Entry<T> temp = this.head.next; while (temp != null) { System.out.print(temp.data + " "); temp = temp.next; } System.out.println(); } /** * 單鏈表的節點類型 * * @param <T> */ static class Entry<T> { T data; // 鏈表節點的數據域 Entry<T> next; // 下一個節點的引用 public Entry(T data, Entry<T> next) { this.data = data; this.next = next; } } /** * 頭節點的類型 * * @param <T> */ private static class HeadEntry<T> extends Entry<T> { int cnt; // 用來記錄節點的個數 public HeadEntry(int cnt, T data, Entry<T> next) { super(data, next); this.cnt = cnt; } } /** * 合併兩個有序的單鏈表 * * @param link */ public void merge(Link<T> link) { Entry<T> p = this.head; Entry<T> p1 = this.head.next; Entry<T> p2 = link.head.next; // 比較p1和p2節點的值,把值小的節點掛在p的後面 while (p1 != null && p2 != null) { if (p1.data.compareTo(p2.data) >= 0) { p.next = p2; p2 = p2.next; } else { p.next = p1; p1 = p1.next; } p = p.next; } if (p1 != null) { // 鏈表1還有剩餘節點 p.next = p1; } if (p2 != null) { // 鏈表2還有剩餘節點 p.next = p2; } } }