前面講過線性表中順序表和鏈表的實現
和性質。可是在數據結構與算法中,雙向鏈表不管在考察仍是運用中都佔有很大的比例,筆者旨在經過本文與讀者一塊兒學習分享雙鏈表相關知識。html
邏輯上沒有區別。他們均是完成線性表的內容。主要的區別是結構上的構造有所區別。 對於單鏈表:java
data
。和next
後驅節點(指針)。也就是這個單鏈表想要一些遍歷的操做都得經過前節點
—>後節點
。對於雙鏈表:node
data
,指向後方的next
(指針)。它擁有單鏈表的全部操做和內容。可是他還有一個前驅節點pre
(指針)。帶頭節點
的。帶頭節點能夠方面首位的插入和刪除。而此次咱們抱着學習的態度搞清鏈表故該雙鏈表是不帶頭節點
的.tail
。這樣咱們就接觸了幾乎全部類型啦!遇到啥也不怕了。因此咱們構造的這個雙鏈表的的性質:算法
對於node節點:安全
class node<T> { T data; node<T> pre; node<T> next; public node() { } public node(T data) { this.data = data; } }
對於鏈表:數據結構
public class doubleList<T> { private node<T> head;// 頭節點 private node<T> tail;// 尾節點 private int length; //各類方法 }
頭插
、尾插
、中間插
。而且還要考慮其中的一些細節處理。指針的運算。防止鏈表崩掉。由於這些操做若是不當每每會對鏈表的結構和證悟性帶來致命的打擊。而像查找
那些邏輯稍微簡單
。也很容易排查錯誤。null
的。那麼對於這個不帶頭節點
的雙鏈表而言。它的head
始終指向第一個真實有效的數據。tail也是如此。那麼在最初沒數據的時候固然要head=null,而且tail=head
。(tail和head須要在一個鏈上)。public doubleList() { head = null; tail = head; length = 0; }
head
和tail
均爲null。但head和tail又須要實實在在指向鏈表中的真實數據(帶頭指針就不須要考慮)。因此這時候就新建一個node
讓head、tail等於它。node<T> teamNode = new node(data); if (isEmpty()) { head = teamNode; tail = teamNode; }
對於頭插入來講。步驟很簡單,只需考慮head節點的變化。ide
從新賦值
)對於尾插入來講。只需考慮尾節點tail節點的變化。學習
從新賦值
等於node便可)對於編號插入來講。要考慮查找和插入兩部,而插入既和head無關也和tail無關。測試
pre
。和後一個節點after
整個流程的動態圖爲:優化
不管頭刪仍是尾刪,遇到單節點刪除的須要將鏈表重新初始化!
if (length == 1)// 只有一個元素 { head = null; tail = head; length--; }
頭刪除須要注意的就是刪除不爲空時候頭刪除只和head節點有關
大體分爲:
斷絕關係
)head
就指向咱們須要的第一個節點了。若是有須要處理內存的語言就能夠把第一個被孤立的節點刪除了)
尾刪除須要注意的就是刪除不爲空時候尾刪除只和tail節點有關。記得在普通鏈表中,咱們刪除尾節點須要找到尾節點的前驅節點。須要遍歷整個表。而雙向鏈表能夠直接從尾節點遍歷到前面。
刪除的時tail所在位置的點。也就是tail所在節點要斷絕和雙鏈表的關係。
tail.pre.next=null
尾節點的前一個節點(pre)的後驅節點等於nulltail=tail.pre
尾節點指向它的前驅節點,此時尾節點因爲步驟1
next已經爲null。完成刪除普通刪除須要重點掌握,由於前兩個刪除都是普通刪除的一個特例而已。(普通刪除要確保不是頭刪除和尾刪除)
team.next
是要刪除的節點)team.next.next.pre=team
.(欲被刪除節點的後一個節點的前驅指向team,雙向鏈表須要處理pre和next。這步處理了pre)team.next=team.next.next;
此時team.next也跳過被刪除節點。代碼:
package LinerList; /* * 不帶頭節點的 */ public class doubleList<T> { class node<T> { T data; node<T> pre; node<T> next; public node() { } public node(T data) { this.data = data; } } private node<T> head;// 頭節點 private node<T> tail;// 尾節點 private int length; public doubleList() { head = null; tail = head; length = 0; } boolean isEmpty() { return length == 0 ? true : false; } void addfirst(T data) { node<T> teamNode = new node(data); if (isEmpty()) { head = teamNode; tail = teamNode; } else { teamNode.next = head; head = teamNode; } length++; } void add(T data)// 尾節點插入 { node<T> teamNode = new node(data); if (isEmpty()) { head = teamNode; tail = teamNode; } else { tail.next = teamNode; teamNode.pre=tail; tail = teamNode; } length++; } int length() { return length; } T getElum(int index)//爲了簡單統一從頭找 { node<T> team=head; for(int i=0;i<index;i++)//不帶頭節點 遍歷次數-1 { team=team.next; } return team.data; } void add(int index, T data)// 編號插入 { if (index == 0) { addfirst(data); } else if (index == length) { add(data); } else {// 重頭戲 node teampre = head;// 爲插入的前qu for (int i = 0; i < index -1; i++)// 無頭節點,index-1位置找到前驅節點 { teampre = teampre.next; } node<T> team = new node(data);// a c 中插入B 找打a team.next = teampre.next;// B.next=c teampre.next.pre = team;// c.pre=B team.pre = teampre;// 關聯a B teampre.next = team; length++; } } void deletefirst()// 頭部刪除 { if (length == 1)// 只有一個元素 { head = null; tail = head; length--; } else { head = head.next; length--; } } void deletelast() { if(length==1) { head=null; tail=head; length--; } else { tail.pre.next=null; tail=tail.pre; length--; } } void delete(int index) { if(index==0)deletefirst(); else if (index==length-1) { deletelast(); } else {//刪除 爲了理解統一從頭找那個節點 node<T>team=head; for(int i=0;i<index-1;i++) { team=team.next; } //team 此時爲要刪除的前節點 a c 插入B a爲team team.next.next.pre=team;//c的前驅變成a team.next=team.next.next;//a的後驅變成c length--; } } void set(int index,T data) { node<T>team=head; for(int i=0;i<index-1;i++) { team=team.next; } team.data=data; } @Override public String toString() { node<T> team = head; String vaString = ""; while (team != null) { vaString += team.data + " "; team = team.next; } return vaString; } }
測試:
package LinerList; public class test { public static void main(String[] args) throws Exception { // TODO Auto-generated method stub System.out.println("線性表測試:"); doubleList<Integer> list = new doubleList<Integer>(); list.add(66); list.addfirst(55); list.add(1, 101); list.add(-22); list.add(555); list.addfirst(9999); System.out.println(list.toString() + " lenth " + list.length());// 9999 55 101 66 -22 555 // System.out.println(list.getElum(0)+" "+list.getElum(2)+" "+list.getElum(4)); list.deletefirst(); System.out.println(list.toString() + " lenth " + list.length());// 55 101 66 -22 555 lenth 5 list.delete(1); System.out.println(list.toString() + " length " + list.length());// 55 66 -22 555 length 4 list.delete(1); System.out.println(list.toString() + " length " + list.length());// 55 -22 555 length 3 list.deletelast(); System.out.println(list.toString() + " lenth " + list.length());// 55 -22 lenth 2 list.deletelast(); System.out.println(list.toString() + " lenth " + list.length());// 55 lenth 1 list.deletelast(); System.out.println(list.toString() + " lenth " + list.length());// lenth 0 System.err.println("歡迎關注公衆號:bigsai"); } }
結果圖
插入、刪除順序問題:
完成相同的結果
便可!畫個圖
。你也能夠先建一個節點,用變量名完成操做,可能會更容易一些。好比刪除操做,你找到pre
節點(刪除前的節點)。你能夠node delete=pre.next
,node next=delete.next
。這樣你直接操做pre。delete。next三個節點會更簡單。能夠參考筆者的代碼實現
)。其餘操做問題:
並無寫的太多
。好比查找時候能夠根據長度判斷這個鏈表從頭查找
仍是從尾查找
。另外,代碼寫的可能不是太好,鏈表也沒考慮線程安全問題。算法效率可能不太優。若是有什麼改進或者漏洞還請大佬指出!
最後(last but not least):
關注
一下把。我的公衆號交流:bigsai