1. 程式人生 > >日常學習隨筆-自定義了一個雙鏈表(註釋蠻詳細的)

日常學習隨筆-自定義了一個雙鏈表(註釋蠻詳細的)

明顯 ret 含義 合法性 如何 author 移除 rev for

一、雙鏈表結構

  最近總會抽出一些零碎的時間片段,嘗試按照自己的想法自定一了一個雙鏈表結構的集合。我發現,數組、單鏈表或者雙鏈表,乃至其他結構,本質上就是一種思想,只要遵循結構的核心思想,實現方法會有很多種。數組和單鏈表就不多說了,前幾篇也嘗試自定義了,就雙鏈表來說,就可以定義如下幾種結構:

(1)頭結點+...數據結點.....+尾結點

(2)頭結點+...數據結點.....

(3)頭結點+...數據結點.....頭結點

(4)頭結點+...數據結點.....+尾結點....頭結點

(5)其他結構

本篇使用的是第(1)中結構,結構千千萬,效率差異也很大,只要明白雙鏈表的含義:

(從網上找到了幾張圖,也算很清晰,如下)

雙鏈表結構:結構(3)

技術分享圖片

如何刪除一個結點

技術分享圖片

如何新增一個結點

技術分享圖片

接下來,我就直接貼出雙鏈表-結構(1)的源碼了哈!

二、雙鏈表:MyDoubleLinkedDefin源碼

雙鏈表類:MyDoubleLinkedDefin.java

  1 package com.xfwl.algorithmAnalysis.linklsit;
  2 /**
  3  * 數據結構學習-雙鏈表
  4  * @function  自定義一個雙鏈表
  5  * @author 小風微涼
  6  * @time  2018-5-14 下午1:11:11
  7  */
  8 public class MyDoubleLinkedDefin<T> {    
9 /** 10 * 當前雙鏈表的頭結點 11 */ 12 private Node head; 13 /** 14 * 當前雙鏈表的尾部結點 15 */ 16 private Node bottom; 17 /** 18 * 當前鏈表的數據變化計數 19 */ 20 private int modCount; 21 /** 22 * 當前鏈表中的結點個數(排除首尾這2個結點) 23 */ 24 private int nodeCount;
25 /** 26 * 內置一個滿足雙鏈表的結點類 27 */ 28 private class Node<T>{ 29 /** 30 * 當前結點存放的數據域 31 */ 32 public T data; 33 /** 34 * 當前結點的前驅結點的引用域 35 */ 36 public Node<T> prev; 37 /** 38 * 當前結點的後驅結點的引用域 39 */ 40 public Node<T> next; 41 /** 42 * 結點構造器 43 */ 44 @SuppressWarnings("unchecked") 45 public Node(T data,Node prev,Node next){ 46 this.data=data; 47 this.prev=prev; 48 this.next=next; 49 } 50 } 51 /** 52 * 構造器 53 */ 54 @SuppressWarnings("unchecked") 55 public MyDoubleLinkedDefin(){ 56 //初始化計數 57 modCount=0; 58 nodeCount=0; 59 //初始化頭結點和尾結點 60 head=new Node<T>(null,null,null); 61 bottom=new Node<T>(null,null,null); 62 //清空雙鏈表的數據:重置頭尾結點的關聯關系 63 this.reset(); 64 System.out.println("頭結點:"+head); 65 System.out.println("尾結點:"+bottom); 66 } 67 /** 68 * 重置雙鏈表,重置頭結點,尾結點 69 */ 70 @SuppressWarnings("unchecked") 71 private void reset(){ 72 //重置頭結點 73 this.head.data=null; 74 this.head.prev=null; 75 this.head.next=bottom; 76 //重置尾結點 77 this.bottom.data=null; 78 this.bottom.next=null; 79 this.bottom.prev=head; 80 } 81 /** 82 * 向雙鏈表中添加一個新的結點(追加到尾部) 83 * @param data 84 */ 85 public void add(T data){ 86 //判斷當前數據的合法性 87 if(data==null){ 88 throw new IllegalArgumentException("添加的參數不合法!"); 89 } 90 //添加規則:添加當前結點到尾部結點的前驅結點 91 Node curNode=new Node(data,this.bottom.prev,this.bottom); 92 curNode.prev.next=curNode; 93 this.bottom.prev=curNode; 94 //計數++ 95 modCount++; 96 nodeCount++; 97 System.out.println("新加結點的前驅結點:"+curNode.prev); 98 System.out.println("新加結點的後驅結點:"+curNode.next); 99 } 100 /** 101 * 向雙鏈表中的指定索引位置添加一個新的結點 102 * @param index 位置索引值 103 * @param data 結點數據域存儲的值 104 * 分析說明: 105 * 鏈表和數組的結構明顯存在差異,數組很容易根據索引下標的值找到指定位置的元素,但是鏈表卻不可以, 106 * 結構使然,所以只能從頭結點,或者尾部結點開始向下或向上找到指定位置的元素。 107 * 這裏我提出一個想法,能不能在雙鏈表中再維護一個數組,這個數組用於查找指定位置的元素,而鏈表結構用於 108 * 新增,刪除,修改元素!不過這也存在一個很明顯的弊端,那就是,為了保證數組和鏈表結構中得數據統一性,這有 109 * 增加了復雜度,降低了程序的運行效率。顯然有點不合理! 110 */ 111 public void add(int index,T data){ 112 //檢測位置索引的合法性:也可抽成公共一個方法 113 if(index<0 || index>= this.nodeCount){ 114 throw new IllegalArgumentException("位置索引值不在合理範圍內!"); 115 } 116 //創建一個新結點 117 Node newNode=new Node(data,null,null); 118 //找到要插入位置的結點 119 Node tmpNode=this.findNode(index); 120 //開始移動 121 try{ 122 tmpNode.prev.next=newNode; 123 newNode.prev=newNode; 124 newNode.next=tmpNode; 125 tmpNode.prev=newNode; 126 }catch(Exception e){ 127 e.printStackTrace(); 128 throw e; 129 }finally{ 130 //計數++ 131 modCount++; 132 nodeCount++; 133 } 134 } 135 /** 136 * 從雙鏈表中移除指定索引位置的結點 137 * @param index 索引位置 138 */ 139 public void remove(int index){ 140 //檢測位置索引的合法性:也可抽成公共一個方法 141 if(index<0 || index>= this.nodeCount){ 142 throw new IllegalArgumentException("位置索引值不在合理範圍內!"); 143 } 144 //找到指定位置的結點 145 Node<T> tmpNode=this.findNode(index); 146 //處理結點 147 tmpNode.prev.next=tmpNode.next; 148 tmpNode.next.prev=tmpNode.prev; 149 //計數++ 150 modCount--; 151 nodeCount--; 152 } 153 /** 154 * 獲取當前鏈表的尺寸大小 155 * @return 156 */ 157 public int size(){ 158 return this.nodeCount; 159 } 160 /** 161 * 打印雙鏈表 162 */ 163 public void print(){ 164 if(this.nodeCount==0){ 165 System.out.println("當前雙鏈表沒有任何數據!"); 166 }else{ 167 Node tmpNode=head; 168 int count=0; 169 System.out.println("**************開始打印***************************"); 170 while(tmpNode.next!=null){ 171 tmpNode=tmpNode.next; 172 if(count==this.nodeCount){//過濾尾結點 173 break; 174 } 175 System.out.println("索引值:"+count+",結點數據:"+tmpNode.data); 176 count++; 177 } 178 System.out.println("***************打印結束**************************"); 179 } 180 } 181 /** 182 * 找到雙鏈表中指定位置的結點(先折半後再查找) 183 * @param index 指定的位置索引值 184 * @return 返回位置上的結點 185 */ 186 public Node<T> findNode(int index){ 187 //檢測位置索引的合法性 188 if(index<0 || index>= this.nodeCount){ 189 throw new IllegalArgumentException("位置索引值不在合理範圍內!"); 190 } 191 //取鏈表一半的數量(計算的是索引值範圍) 192 int midCount=(this.nodeCount)/2; 193 //計數起點 194 int count=0; 195 //拿到第一個結點數據 196 Node tmp=head.next; 197 //如果在前一半範圍:[0,midCount] 198 if(index>=0 && index<=midCount){ 199 while(index!=count){//要找的位置!=計數位置 一直累計疊加到相等位置 200 tmp=tmp.next; 201 count++; 202 } 203 }else if(index>midCount && index<this.nodeCount){//如果在後一半範圍內:[midCount,this.nodeCount) 204 //計數起點調高 205 count=this.nodeCount-1; 206 //從尾結點向前推 207 tmp=bottom.prev; 208 while(index!=count){//要找的位置!=計數位置 一直累計疊加到相等位置 209 tmp=tmp.prev; 210 count--; 211 } 212 } 213 return tmp; 214 } 215 /** 216 * 找到雙鏈表中的指定結點所在的位置 217 * @param node 要檢索的結點對象 218 * @return 返回位置索引值 219 * 優化意見: 220 * 這裏的實現方法很中規中矩,可以優先判斷當前檢索的結點是否是頭尾結點,過濾常用的檢索情況。 221 */ 222 public int findNodeIndex(Node node){ 223 int index=0; 224 //拿到頭結點 225 Node tmp=head; 226 while(tmp.next!=null){ 227 tmp=tmp.next; 228 index++; 229 } 230 return index; 231 } 232 }

測試運行類:Test2.java

 1 package com.xfwl.algorithmAnalysis.linklsit;
 2 /**
 3  * 雙鏈表學習測試:
 4  * @function  
 5  * @author 小風微涼
 6  * @time  2018-5-17 下午7:06:16
 7  */
 8 public class Test2 {
 9     /**
10      * 開始測試
11      * @param args
12      */
13     public static void main(String[] args) {
14         MyDoubleLinkedDefin<Object> myList=new MyDoubleLinkedDefin<>();
15         //按照順序加入結點
16         myList.add("第1個結點");
17         myList.add("第2個結點");
18         myList.add("第3個結點");
19         myList.add("第4個結點");
20         myList.add("第5個結點");
21         myList.print();
22         //在第三個索引值上插入一個結點
23         myList.add(4,"第6個結點");
24         myList.print();
25         //移除一個結點
26         System.out.println("--------remove測試---------");
27         for(int i=0;i<6;i++){
28             myList.remove(0);
29             myList.print();
30         }
31     }
32 }

運行結果:

頭結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@6504e3b2
尾結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
新加結點的前驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@6504e3b2
新加結點的後驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
新加結點的前驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@626b2d4a
新加結點的後驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
新加結點的前驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@5e91993f
新加結點的後驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
新加結點的前驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@1c4af82c
新加結點的後驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
新加結點的前驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@379619aa
新加結點的後驅結點:com.xfwl.algorithmAnalysis.linklsit.MyDoubleLinkedDefin$Node@515f550a
**************開始打印***************************
索引值:0,結點數據:第1個結點
索引值:1,結點數據:第2個結點
索引值:2,結點數據:第3個結點
索引值:3,結點數據:第4個結點
索引值:4,結點數據:第5個結點
***************打印結束**************************
**************開始打印***************************
索引值:0,結點數據:第1個結點
索引值:1,結點數據:第2個結點
索引值:2,結點數據:第3個結點
索引值:3,結點數據:第4個結點
索引值:4,結點數據:第6個結點
索引值:5,結點數據:第5個結點
***************打印結束**************************
--------remove測試---------
**************開始打印***************************
索引值:0,結點數據:第2個結點
索引值:1,結點數據:第3個結點
索引值:2,結點數據:第4個結點
索引值:3,結點數據:第6個結點
索引值:4,結點數據:第5個結點
***************打印結束**************************
**************開始打印***************************
索引值:0,結點數據:第3個結點
索引值:1,結點數據:第4個結點
索引值:2,結點數據:第6個結點
索引值:3,結點數據:第5個結點
***************打印結束**************************
**************開始打印***************************
索引值:0,結點數據:第4個結點
索引值:1,結點數據:第6個結點
索引值:2,結點數據:第5個結點
***************打印結束**************************
**************開始打印***************************
索引值:0,結點數據:第6個結點
索引值:1,結點數據:第5個結點
***************打印結束**************************
**************開始打印***************************
索引值:0,結點數據:第5個結點
***************打印結束**************************
當前雙鏈表沒有任何數據!

自認為代碼註釋的還算仔細的哈,如有不足,不吝指正!

日常學習隨筆-自定義了一個雙鏈表(註釋蠻詳細的)