1. 程式人生 > 其它 >Java 連結串列API

Java 連結串列API

Java 連結串列

1、什麼是連結串列?

連結串列是一種物理儲存單元上非連續、非順序的儲存結構,資料元素的邏輯順序是通過連結串列中的指標連線次序實現的。

每一個連結串列都包含多個節點,節點又包含兩個部分:

1)一個是資料域(儲存節點含有的資訊)

2)一個是引用域(儲存下一個節點或者上一個節點的地址)

連結串列的理解示意圖

 

 

2、連結串列的特點是什麼?

獲取資料麻煩,需要遍歷查詢,比陣列慢

方便插入、刪除

 

3、連結串列的實現原理

建立一個節點類,其中節點類包含兩個部分,第一個是資料域(你到時候要往節點裡面儲存的資訊),第二個是引用域(相當於指標,單向連結串列有一個指標,指向下一個節點;雙向連結串列有兩個指標,分別指向下一個和上一個節點)

建立一個連結串列類,其中連結串列類包含三個屬性:頭結點、尾節點和大小,方法包含新增、刪除、插入等等方法。

 

單向連結串列的節點類:

1 2 3 4 5 6 7 8 public class Node {     public Object data;     public Node next;       public Node(Object e){         this.data = e;     } }

 

雙向連結串列的節點類:

1 2 3 4 5 6 7 8 9 10 11 12 13 public class Node {
    public Object e;     public Node next;     public Node pre;     public Node(){       }     public Node(Object e){         this.e = e;         next = null;         pre = null;     } }

 

4、如何自己寫出一個連結串列?

程式碼如下(以雙向連結串列為例,有詳細的註釋,單向連結串列同理):

1)首先,建立了一個節點類

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package MutuLink;   public class Node {     public Object e;     public Node next;     public Node pre;     public Node(){       }     public Node(Object e){         this.e = e;         next = null;         pre = null;     } }

 

2)然後,建立了一個連結串列類

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 package MutuLink;   public class MyList {     private Node head;     private Node tail;     private int size = 0;       public MyList() {         head = new Node();         tail = new Node();         head.next =null;         tail.pre = null;     }       public boolean empty() {         if (head.next == null)             return true;         return false;     }     //找到所找下標節點的前一個節點     public Node findpre(int index){         Node rnode = head;         int dex = -1;         while(rnode.next != null){             //找到了插入節點的上一個節點             if( dex== index - 1){                 return rnode;             }             rnode = rnode.next;             dex++;         }         return null;     }     public Node findthis(int index){         Node rnode = head;         //把rnode想象為指標,dex為指向的下標,這個地方很容易錯,因為當指向最後一個節點時沒有判斷IF就跳出迴圈了         int dex = -1;         while(rnode.next != null){             if(dex == index)             return rnode;             rnode = rnode.next;             dex++;         }         if(dex == size - 1){             return rnode;         } //        Node test = new Node(new Students("haha",1,2));         return null;     }       // 往連結串列末尾加入節點     public void add(Object e) {         Node node = new Node(e);         Node rnode = head;         //如果是空連結串列的話插入一個節點,這個節點的pre不能指向上一個節點,必須指空         if (this.empty()) {             rnode.next = node;             rnode.next.pre = null;             tail.pre = node;             size++;         } else {             while (rnode.next != null)                 rnode = rnode.next;             rnode.next = node;             node.pre = rnode;             tail.pre = node;             size++;         }     }     //往連結串列的某一個標插入一個節點     public boolean add(int index,Object e){         if(index <0||index>=size)             return false;         Node node = new Node(e);         Node prenode = this.findpre(index);         node.next = prenode.next;         prenode.next.pre = node;         prenode.next = node;         node.pre = prenode;         size++;         return true;     }     public boolean add(int index,MyList myl){         if(index <0 || index >= size)             return false;         Node prenode = this.findpre(index); //        myl.tail.pre.next = prenode.next; //        prenode.pre = myl.tail.pre; //        tail.pre = null; //        prenode.next = myl.head.next; //        myl.head.next.pre = prenode; //        head.next = null;         myl.tail.pre.next = prenode.next;         prenode.next.pre = myl.tail.pre.pre;         myl.head.next.pre = prenode.pre;         prenode.next = myl.head.next;         myl.head = null;         myl.tail = null;         size+=myl.size;         return true;     }       public Object remove(int index){         Object ob= this.get(index);         if(index <0 || index >= size)             return null;         //特殊情況,當移除節點是最後一個節點的時候         //較為複雜通過畫圖來寫程式碼         if(index == size - 1){             Node prenode = this.findpre(index);             this.tail.pre = this.tail.pre.pre;             this.tail.pre.next.pre = null;             this.tail.pre.next =null;             size--;             return ob;         }         //比較複雜,通過畫圖解決         else{             Node prenode = this.findpre(index);             prenode.next = prenode.next.next;             prenode.next.pre.next = null;             prenode.next.pre = prenode.next.pre.pre;             size--;             return ob;         }     }         public Object get(int index){         Node thisnode = this.findthis(index);         return thisnode.e;     }     public int size(){         return size;     } }

 

3)最後,測試

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package MutuLink;   import java.util.Random;   public class manage {     public static void main(String[] args) {         String name = "";         int credit;         int age;         int size;         MyList myl = new MyList();         Random random = new Random();         size = random.nextInt(5) + 1;         for (int i = 0; i < size; i++) {             credit = random.nextInt(5);             age = random.nextInt(5) + 18;             for (int j = 0; j < 4; j++) {                 name += (char) (random.nextInt(26) + 97);             }             Students stu = new Students(name, credit, age);             myl.add(stu);             name = "";         }           System.out.println("Size of myl1 is "+ myl.size());         for(int i = 0; i < myl.size() ;i++){             Students stu2 = (Students) myl.get(i);             stu2.show();         } //        //測試能否在連結串列末尾加入節點(成功) //        for(int i = 0; i < myl.size() ;i++){ //            Students stu2 = (Students) myl.get(i); //            stu2.show(); //        } //        //測試能否通過下標加入一個節點(成功) //        Students stu3 = new Students("cyt",5,18); //        myl.add(1, stu3); //        System.out.println("Size is "+ myl.size()); //        for(int i = 0; i < myl.size() ;i++){ //            Students stu2 = (Students) myl.get(i); //            stu2.show(); //        }           MyList myl2 = new MyList();         size = random.nextInt(5) + 1;         for (int i = 0; i < size; i++) {             credit = random.nextInt(5);             age = random.nextInt(5) + 18;             for (int j = 0; j < 4; j++) {                 name += (char) (random.nextInt(26) + 97);             }             Students stu2 = new Students(name, credit, age);             myl2.add(stu2);             name = "";         }         System.out.println("Size is of myl2 "+ myl2.size());         for(int i = 0; i < myl2.size() ;i++){             Students stu2 = (Students) myl2.get(i);             stu2.show();         }           myl.add(1, myl2);         System.out.println("Size is of myl1 "+ myl.size());         for(int i = 0; i < myl.size() ;i++){             Students stu2 = (Students) myl.get(i);             stu2.show();         }     } }

 

4)結果輸出

Java 實現連結串列類 LinkList

順序陣列表作為資料儲存結構有很多優點,最重要的就是順序表的元素是隨機存取的。

但是順序表也有很多缺點,主要的缺點有兩個:

一是插入和刪除的效率低下,因為插入和刪除操作需要變化部分元素的位置,時間複雜度是O(n)。

二是資料一旦建立就不能改變大小,如果想擴大陣列的容量,那麼應該建立一個新的更大的新陣列,將原陣列複製到新陣列中,這是十分不方便的。

 

為了解決上述問題,連結串列就出現了,連結串列的主要特點是:

1、連結串列的大小是動態的,隨著資料的增加或者刪除動態改變長度。

2、連結串列的插入、刪除十分高效,對於經常需要插入和刪除資料的應用,非常適合使用連結串列作為資料儲存結構。

順序表和連結串列都屬於線性表。

 

Java 連結串列API

在看程式碼之前,我們先看看連結串列需要完成什麼任務,也就是連結串列的API,

這樣有助於理解程式碼,也有助於快速查詢自己想實現的功能。

  • LinkList(): 建構函式
  • clear(): 刪除整個列表
  • removeAll(): 刪除整個列表,呼叫clear函式來實現
  • getNode(int i): 獲得邏輯上i號節點,返回節點
  • get(int i): 獲得邏輯上i號節點的值,返回值
  • set(int i,T x): 修改i號節點的值
  • add(int i,T x): 將值為x的節點插入到i號位置
  • add(T key): 在連結串列尾部插入元素key
  • addBack(T key): 在連結串列尾部插入元素key,和add(T key)函式的作用一樣
  • addFront(T key): 在連結串列首部插入元素key
  • remove(int i): 刪除i號節點,並返回i號節點對應的值
  • remove(): 過載remove,刪除頭節點
  • removeFront(): 刪除連結串列頭節點,與remove()函式同義
  • removeBack(): 刪除連結串列尾節點
  • addSort(T value): 將值value按從小到大的排序方式插入連結串列
  • sort(): 對連結串列按照從小到大的順序進行排序
  • indexOf(int begin,int end,T key): 在索引begin和end之間查詢值key,返回邏輯編號
  • search(T key): 功能同indexOf,遍歷整個連結串列,一般不使用,主要用於實現字典
  • contains(T key): 判斷連結串列中是否存在值為key節點
  • toString(): 將連結串列中的值轉換成字串
  • toArray(): 將連結串列轉換成Object陣列
  • toArray(E[] a): 將單鏈錶轉化為特定型別的陣列,使用了函式泛型
  • iterator(): 返回迭代器物件

 

Java 連結串列程式碼

要注意的點:

1、使用程式碼的時候,記得把package改成自己的檔案所在的包名。

2、程式碼中LinkList繼承的父類AbsList在這裡沒有寫,因為寫程式碼時,在順序表SeqList中實現了AbsList類,這個AbsList預設是default,也就是包訪問許可權(default修飾的類是包訪問的),然後我把SeqList和LinkList兩個類放在一個包中了。所以其實LinkList使用的AbsList其實是SeqList類中的AbsList,所以這裡就使用的是上篇文章中的SeqList中的AbsList,所以如果想讓這個類可以使用,去上面文章中找AbsList類copy一下就好了。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 /*  * created on April 16 14:40 2019  *  * @author:lhy  */ package DS;   import java.util.Iterator;     //建立節點類Lnode<T> class Lnode<T> implements Comparable<Lnode<T>> {     public T data;  // 節點值     public Lnode<T> next; // 儲存下個節點       // Lnode<T>建構函式     public Lnode(T key) {         data = key;         next = null;     }       public Lnode(T key, Lnode<T> next) {         data = key;         this.next = next;     }       // 判斷節點值是否相等     public boolean equals(Object e) {         Lnode<T> node = (Lnode<T>) e;         return data.equals(node.data);     }       // 實現Comparable的compareTo方法,用於比較連結串列節點比較大小     public int compareTo(Lnode<T> e) {         Comparable<T> x;         if(data instanceof Comparable) {             x=(Comparable<T>)data;             return (int)x.compareTo(e.data);         }         else {             throw new ClassCastException("型別無法比較");         }     }           //將該節點的值變成字串格式     public String toString() {         return data.toString();     } }   /*  * LinkList API介紹  *  * LinkList(): 建構函式  * clear(): 刪除整個列表  * removeAll(): 刪除整個列表,呼叫clear函式來實現  * getNode(int i): 獲得邏輯上i號節點  * get(int i): 獲得邏輯上i號節點的值  * set(int i,T x): 修改i號節點的值  * add(int i,T x): 將值為x的節點插入到i號位置  * add(T key): 在連結串列尾部插入元素key  * addBack(T key): 在連結串列尾部插入元素key,和add(T key)函式的作用一樣  * addFront(T key): 在連結串列首部插入元素key  * remove(int i): 刪除i號節點,並返回i號節點對應的值  * remove(): 過載remove,刪除頭節點  * removeFront(): 刪除連結串列頭節點,與remove()函式同義  * removeBack(): 刪除連結串列尾節點  * addSort(T value): 將值value按從小到大的排序方式插入連結串列  * sort(): 對連結串列按照從小到大的順序進行排序  * indexOf(int begin,int end,T key): 在索引begin和end之間查詢值key,返回邏輯編號  * search(T key): 功能同indexOf,遍歷整個連結串列,一般不使用,主要用於實現字典  * contains(T key): 判斷連結串列中是否存在值為key節點  * toString(): 將連結串列中的值轉換成字串  * toArray(): 將連結串列轉換成Object陣列  * toArray(E[] a): 將單鏈錶轉化為特定型別的陣列,使用了函式泛型  * iterator(): 返回迭代器物件  */ public class LinkList<T> extends AbsList<T> implements Iterable<T> {     //LinkList繼承了SeqList中的AbsList抽象類,繼承了Iterable介面     Lnode<T> first=null,last=null; //頭指標和尾指標     Iterator<T> iterator=null; //指向當前節點的迭代器     //建構函式     public LinkList() {         first=last=null;         length=0;         this.iterator=new LinkIterator();     }           //比較器,供內部使用     private int compare(Lnode<T> a,Lnode<T> b) {         return a.compareTo(b);     }     //刪除整個列表     public void clear() {         first=last=null;         length=0;     }           //刪除整個列表,呼叫clear函式來實現     public void removeAll() {         clear();     }           //獲得邏輯上的i號節點     public Lnode<T> getNode(int i){         //判斷i號節點是否越界         if(i<0||i>length-1) {             return null;         }         //判斷i號是否為頭節點(其實可以不用判斷)         if(i==0) {             return first;         }         else {             Lnode<T> p=first;             int j=0;             while (p!=null&&j<i) {                 p=p.next;                 j++;             }             return p;         }               }           //獲得i號節點的值,呼叫getNode完成具體工作     public T get(int i) {         Lnode<T> pLnode=getNode(i);         if(pLnode==null)             return null;         else             return pLnode.data;     }           //修改i號節點的值     public boolean set(int i,T x) {         Lnode<T> p=getNode(i);         if(p==null) {             return false;         }         else {             p.data=x;             return true;         }     }           //將值為x的節點插入到i號位置     public void add(int i,T x) {         Lnode<T> p,s;         int j=i-1;         s=new Lnode<T>(x,null);         //在空連結串列中插入節點s         if(first==null||length==0) {             first=s;             last=s;         }                   //在頭節點之前插入節點s,即i=0時         else if(j<0) {             s.next=first;             first=s;         }         //在連結串列尾部插入節點s         else if(j>=length-1) {             last.next=s;             last=s;         }         //在連結串列中間插入節點s         else {             p=getNode(j);             s.next=p.next;             p.next=s;         }         length++;     }           //過載add()函式,在連結串列尾部插入元素key     public void add(T key) {         add(length,key);     }           //在連結串列尾部插入元素key,和add(T key)函式的作用一樣     public void addBack(T key) {         add(length,key);     }           //在連結串列首部插入元素key     public void addFront(T key) {         add(0,key);     }           //刪除i號節點,並返回i號節點對應的值,通過呼叫removeNode實現     public T remove(int i) {         Lnode<T> p=removeNode(i);         if(p!=null)             return p.data;         else             return null;     }           //核心演算法,刪除邏輯上的i號節點,內部使用,返回Lnode<T>     protected Lnode<T> removeNode(int i){         Lnode<T> p,q;         //判斷連結串列是否為空         if(first==null) {             return null;         }         //刪除頭節點         if(i==0) {             p=first;             first=first.next;             length-=1;             return p;         }         //刪除中間節點或尾節點         if(i>=1 && i<=length-1) {             //首先獲得i-1位置所在的節點             p=getNode(i-1);             //獲得i節點位置上的節點             q=p.next;             p.next=q.next;             if(q==last) {                 last=p;             }             length--;             return q;         }         return null;     }           //過載remove,刪除頭節點     public T remove() {         return removeNode(0).data;     }           //刪除頭節點,與remove()函式同義     public T removeFront() {         return removeNode(0).data;     }           //刪除尾節點     public T removeBack() {         return removeNode(length-1).data;     }           //將值value按從小到大的排序方式插入連結串列,呼叫insertOrder實現     public void addSort(T value) {         Lnode<T> s=new Lnode(value);         insertOrder(s);     }           //有序插入的核心演算法     private void insertOrder(Lnode<T> s) {         Lnode<T> p1,p2;         length++;         //在空連結串列中插入節點         if(first==null) {             first=s;             last=first;             return ;         }         //小於頭節點的值,將節點插入到頭節點之前         if(compare(s, first)<0) {             s.next=first;             first=s;             return ;         }         //大於最後一個節點值,將節點在連結串列尾部插入         if(compare(s, last)>0) {             last.next=s;             last=s;             return ;         }         //被插入的節點p在p1和p2之間,p1在前,p2在後         p2=first;         p1=p2;         while(p2!=null) {             //s節點比p2節點大時,將p1等於p2,p2後移一個位置,直到s小於等於p2時停止迴圈,這時s節點的值在p1和p2之間             if(compare(s, p2)>0) {                 p1=p2;                 p2=p2.next;             }             else {                 break;             }         }         s.next=p2;         p1.next=s;         return;     }           //對連結串列排序     public void sort() {         LinkList<T> sl=new LinkList<T>();//建立一個存放有序序列的連結串列         Lnode<T> p;         p=this.removeNode(0);//取出無序連結串列的頭節點         while(p!=null) {             sl.addSort(p.data);             p=this.removeNode(0);         }         Lnode<T> q=sl.first;         while(q!=null) {             this.add(q.data);             q=q.next;         }     }           //在索引begin和end之間查詢值key,返回邏輯編號     public int indexOf(int begin,int end,T key) {         Lnode<T> p=getNode(begin);//獲取開始節點         int i=begin;         while(p!=null&&i<end) {             if(p.data.equals(key))                 return i;             p=p.next;             i++;         }         return -1;     }           //功能同indexOf,一般不使用,主要用於實現字典     public T search(T key) {         Lnode<T> p=getNode(0);         while(p!=null) {             if(p.data.equals(key)) {                 return p.data;             }             p=p.next;         }         return null;     }           //判斷連結串列中是否存在值為key節點     public boolean contains(T key) {         if(indexOf(0,length,key)==-1) return false;         else return true;     }           //將連結串列中的值轉換成字串     public String toString() {         String string;         Lnode<T> pLnode;         pLnode=first;         string="(";         while(pLnode!=null) {             string+=pLnode.data.toString()+" ";             pLnode=pLnode.next;         }         return string+")";     }           //將連結串列轉換成Object陣列     public Object[] toArray() {         Object[] a=new Object[length];         Lnode<T> pLnode=first;         for(int i=0;i<length;i++) {             a[i]=pLnode.data;             pLnode=pLnode.next;         }         return a;     }           //將單鏈錶轉化為特定型別的陣列,使用了函式泛型     public <E> E[] toArray(E[] a) {         if(a.length<length) {             //建立一個長度為length(和連結串列長度相等),型別為陣列a的元素型別的陣列,並強制轉換成E[]型別             a=(E[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), length);         }         int i=0;         Object[] result=a;         Lnode<T> xLnode=this.first;         for(i=0;i<length;i++) {             result[i]=xLnode.data;             xLnode=xLnode.next;         }         if(a.length>length) {             a[length]=null;         }         return a;     }           //返回迭代器物件     public Iterator<T> iterator(){         return new LinkIterator();     }           //內部類,實現迭代器     private class LinkIterator implements Iterator<T>{         private int index=0;         private Lnode<T> current=first;         public boolean hasNext() {             //在呼叫next()之後,index自增,確保index不等於person的長度             return (index!=length() && current!=null);         }                   public T next() {             T temp=current.data;             current=current.next;             index++;             return temp;         }                   public int nextIndex() {             return index++;//先返回index的值,後加1         }         public void remove() {             //未實現本方法         }     } }

 

使用連結串列示例

建立一個LinkList連結串列物件,對LinkList類中各個函式進行實現檢驗,程式碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 /*  * created on April 16 15:40 2019  *  * @author:lhy  */ package DS;   import java.util.Iterator;   /*  * LinkList API介紹  *  * LinkList(): 建構函式  * clear(): 刪除整個列表  * removeAll(): 刪除整個列表,呼叫clear函式來實現  * get(int i): 獲得邏輯上i號節點的值  * set(int i,T x): 修改i號節點的值  * add(int i,T x): 將值為x的節點插入到i號位置  * add(T key): 在連結串列尾部插入元素key  * addBack(T key): 在連結串列尾部插入元素key,和add(T key)函式的作用一樣  * addFront(T key): 在連結串列首部插入元素key  * remove(int i): 刪除i號節點,並返回i號節點對應的值  * remove(): 過載remove,刪除頭節點  * removeFront(): 刪除連結串列頭節點,與remove()函式同義  * removeBack(): 刪除連結串列尾節點  * addSort(T value): 將值value按從小到大的排序方式插入連結串列  * sort(): 對連結串列按照從小到大的順序進行排序  * indexOf(int begin,int end,T key): 在索引begin和end之間查詢值key,返回邏輯編號  * search(T key): 功能同indexOf,遍歷整個連結串列,一般不使用,主要用於實現字典  * contains(T key): 判斷連結串列中是否存在值為key節點  * toString(): 將連結串列中的值轉換成字串  * toArray(): 將連結串列轉換成Object陣列  * toArray(E[] a): 將單鏈錶轉化為特定型別的陣列,使用了函式泛型  * iterator(): 返回迭代器物件  */   public class Test_LinkList {     public static void main(String[] args) {         LinkList<Integer> linkList=new LinkList<Integer>();         //對linkList賦值         for(int i=0;i<10;i++) {             linkList.add((int)(Math.random()*1000));         }         System.out.println("由系統隨機數生成的連結串列為:"+linkList.toString());         System.out.println("獲取7號節點的連結串列元素為:"+linkList.get(7));         //將下標為1的節點值修改為2333         linkList.set(1,2333);         System.out.println("將1號節點值修改為2333,然後輸出:"+linkList.toString());         //向3號位置插入值為5555的節點         linkList.add(3,5555);         System.out.println("向3號節點位置插入值為5555的節點,輸出連結串列:"+linkList.toString());         //向連結串列尾部插入值為9999的節點         linkList.add(9999);         System.out.println("向連結串列尾部插入值為9999的節點,輸出連結串列:"+linkList.toString());         //向連結串列首部插入值為12345的節點         linkList.addFront(12345);         System.out.println("向連結串列首部插入值為12345的節點,輸出連結串列:"+linkList.toString());         System.out.println("刪除4號節點,返回值為:"+linkList.remove(4));         System.out.println("刪除4號節點後,輸出連結串列"+linkList.toString());         //刪除尾節點         linkList.removeBack();         System.out.println("刪除尾節點後,輸出連結串列:"+linkList.toString());         //對linkList進行排序         linkList.sort();         System.out.println("排序後的連結串列為:"+linkList.toString());         //向連結串列中有序插入節點值為666的節點         linkList.addSort(666);         System.out.println("向連結串列中有序插入節點值為666的節點後,輸出連結串列:"+linkList.toString());         System.out.println("查詢值為666的節點在連結串列中位置為:"+linkList.indexOf(0, linkList.length,666));         System.out.println("判斷連結串列中是否有值為665的節點:"+linkList.contains(665));         //獲得一個連結串列迭代器物件         Iterator<Integer> iterator=linkList.iterator();         //使用迭代器輸出連結串列元素         System.out.print("使用迭代器輸出連結串列元素:");         while(iterator.hasNext()) {             System.out.print(iterator.next()+" ");         }         //清空連結串列         linkList.clear();         System.out.println("\n輸出清空後的連結串列:"+linkList.toString());       } }

執行結果:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 由系統隨機數生成的連結串列為:(834 738 152 568 863 608 495 975 742 475 ) 獲取7號節點的連結串列元素為:975 1號節點值修改為2333,然後輸出:(834 2333 152 568 863 608 495 975 742 475 ) 3號節點位置插入值為5555的節點,輸出連結串列:(834 2333 152 5555 568 863 608 495 975 742 475 ) 向連結串列尾部插入值為9999的節點,輸出連結串列:(834 2333 152 5555 568 863 608 495 975 742 475 9999 ) 向連結串列首部插入值為12345的節點,輸出連結串列:(12345 834 2333 152 5555 568 863 608 495 975 742 475 9999 ) 刪除4號節點,返回值為:5555 刪除4號節點後,輸出連結串列(12345 834 2333 152 568 863 608 495 975 742 475 9999 ) 刪除尾節點後,輸出連結串列:(12345 834 2333 152 568 863 608 495 975 742 475 ) 排序後的連結串列為:(152 475 495 568 608 742 834 863 975 2333 12345 ) 向連結串列中有序插入節點值為666的節點後,輸出連結串列:(152 475 495 568 608 666 742 834 863 975 2333 12345 ) 查詢值為666的節點在連結串列中位置為:5 判斷連結串列中是否有值為665的節點:false 使用迭代器輸出連結串列元素:152 475 495 568 608 666 742 834 863 975 2333 12345 輸出清空後的連結串列:()

 

 

Java 實現順序表類 SeqList

資料結構中的線性儲存結構分為兩大類:順序儲存鏈式儲存

順序儲存對應的是順序表陣列,鏈式儲存對應的是連結串列。

這篇文章主要介紹如何使用Java實現一個順序表。

 

順序表介紹

順序表: 把線性表的元素按照一定邏輯順序依次放在一組地址連續的儲存單元裡,用這種方式儲存的線性表簡稱為 順序表。而連結串列中的元素是沒有放在一組地址連續的儲存單元裡,它們之間的連結是靠指標串一起的。

優點:隨機存取表中元素。隨機存取的意思是對所有元素序號來講都是一樣的計算時間,也就是說,訪問任何一個元素的時間都是相同的。由於每個元素在計算機中佔有的儲存單元是相同的(假設佔有的空間是c),則可知a[i]所在的儲存地址是: Loc(ai)=Loc(a0)+i*c,訪問不同儲存位置的元素時間都是相同的,因此順序標是隨機存取。

缺點:插入和刪除比較慢,不可以增加長度,如想增加長度,則需另開記憶體將原陣列複製到新空間。

 

Java 順序表API

先看看程式碼的API,有助於理解程式碼(雖說程式碼中的註釋很完整。。。):

  • setIncr(int inc): 設定順序表每次增加時增量的大小
  • setCapacity(int newSize): 設定新的最大容量
  • getCapacity(): 獲取陣列的最大容量
  • size(): 獲取順序表已經存放資料的數量,同length()
  • get(int i): 獲取順序表下標為i的元素值
  • set(int i,T x): 修改下標值為i的元素值,即為修改data[i]
  • indexOf(int begin,int end,T value): 在begin和end之間查詢值為value的元素下標值,使用順序查詢法
  • indexOf(T o): 在整個線性表中查詢元素o的下標值
  • indexOf(int begin,T o): 在下標為begin到末尾的線性表中查詢元素o的下標值
  • add(int i,T x): 在i位置插入資料元素x
  • add(T x): 在表尾新增資料元素x
  • append(T x): 在表尾新增資料元素x
  • addSort(T x): 以有序方式向順序表中插入資料元素x
  • sort(): 對順序表從小到大排序
  • remove(int i): 刪除下標為i的元素,並返回被刪除的元素
  • remove(T value): 刪除值為value的元素,並返回被刪除的元素
  • clear(): 清除整個順序表
  • toString(): 將順序錶轉化成字串,便於直接輸出順序表
  • toArray(): 將順序錶轉化成Object陣列,並返回
  • toArray(T[] a): 將順序錶轉換為型別為E的陣列
  • iterator(): 返回一個迭代物件
  • length(): 獲取線性表長度
  • isEmpty(): 判斷線性表是否為空

 

Java 順序表程式碼

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 /*  * created on April 15 8:20 2019  *  * @author:lhy  */   package DS;   import java.util.Arrays; import java.util.Iterator;   import org.w3c.dom.html.HTMLIsIndexElement;   interface MyList<T>{     //介面中每一個方法都是隱式抽象的,介面中所有方法會被隱式指定為public abstract型別(且只能是public abstract)     //介面中的變數會被隱式指定為public static final型別變數     //介面中的函式不能在介面中實現,只能由實現介面的類來實現介面中的方法           boolean isEmpty();//判斷線性表是否為空     int length();//獲得List的元素個數     T get(int i);//取得第i個位置上的資料元素,返回資料元素的值     boolean set(int i,T x);//將第i個位置上的資料元素設定為值x     void add(int i,T x);//位置i及其後的元素依次後移一個位置,然後將x插入到第i個位置     T remove(int i);//刪除第i個位置上的資料元素,位置i+1及其後的元素依次前移一個位置     int indexOf(T x);//獲取資料元素x在表中的下標位置 }   abstract class AbsList<T> implements Iterable<T>,MyList<T>{     //必須實現介面MyList中所描述的所有方法,否則必須宣告為抽象類     protected int length;//length指的是這個順序表已經容納的元素個數     abstract public T get(int i);//返回第i個元素     abstract public boolean set(int i,T x);//設定第i個元素的值為x     abstract public int indexOf(int begin,int end,T o);//在begin和end之間尋找值為o的元素的下標     abstract public void add(int i,T x);//插入x作為第i個元素     abstract public void clear();//清空列表     abstract public T remove(int i);//刪除第i個元素並返回被刪除物件     abstract public Iterator<T> iterator();//返回一個迭代器           //判斷線性表是否為空     public boolean isEmpty() {         return length==0;     }     //獲取線性表長度     public int length(){         return length;     }     //線上性表最後插入x元素     public void add(T x) {         add(length,x);     }     //與add一樣     public void append(T x) {         add(length,x);     }     //在整個線性表中查詢元素o的下標值     public int indexOf(T o) {         return indexOf(0,length,o);     }     //在下標為begin到末尾的線性表中查詢元素o的下標值     public int indexOf(int begin,T o) {         return indexOf(begin,length,o);     }     //刪除第i個元素並返回刪除物件     public T remove(T o) {         return remove(indexOf(o));     } }   /*  * SeqList API介紹  * setIncr(int inc): 設定順序表每次增加時增量的大小  * setCapacity(int newSize): 設定新的最大容量  * getCapacity(): 獲取陣列的最大容量  * size(): 獲取順序表已經存放資料的數量,同length()  * get(int i): 獲取順序表下標為i的元素值  * set(int i,T x): 修改下標值為i的元素值,即為修改data[i]  * indexOf(int begin,int end,T value): 在begin和end之間查詢值為value的元素下標值,使用順序查詢法  * indexOf(T o): 在整個線性表中查詢元素o的下標值  * indexOf(int begin,T o): 在下標為begin到末尾的線性表中查詢元素o的下標值  * add(int i,T x): 在i位置插入資料元素x  * add(T x): 在表尾新增資料元素x  * append(T x): 在表尾新增資料元素x  * addSort(T x): 以有序方式向順序表中插入資料元素x  * sort(): 對順序表從小到大排序  * remove(int i): 刪除下標為i的元素,並返回被刪除的元素  * remove(T value): 刪除值為value的元素,並返回被刪除的元素  * clear(): 清除整個順序表  * toString(): 將順序錶轉化成字串,便於直接輸出順序表  * toArray(): 將順序錶轉化成Object陣列,並返回  * toArray(T[] a): 將順序錶轉換為型別為E的陣列  * iterator(): 返回一個迭代物件  * length(): 獲取線性表長度  * isEmpty(): 判斷線性表是否為空  */   public class SeqList<T> extends AbsList<T> implements Iterable<T> {     //必須實現抽象類AbsList中的所有抽象方法     private int incrementSize;//順序表每次增量的長度     protected Object[] data;//儲存順序表資料的陣列     //預設建構函式     public SeqList() {         this(16);     }     //建構函式,設定初始容量為initLen     public SeqList(int initLen) {         if(initLen<=0) {             initLen=16;         }         length=0;         incrementSize=16;         data=new Object[initLen];     }     //建構函式,用elem陣列初始化順序表     public SeqList(T[] elem) {         length=elem.length;         incrementSize=16;         data=Arrays.copyOf(elem, length);     }     //設定順序表每次容量增加時增量的大小,預設是16     public void setIncr(int inc) {         incrementSize=inc;     }     //設定新的陣列容量     public void setCapacity(int newSize) {         data=Arrays.copyOf(data, newSize);     }     //獲得data陣列的最大容量     public int getCapacity() {         return data.length;     }     //獲取順序表已經存放資料的數量,同length()     public int size() {         return length;     }     //取得順序表下標為i的元素值,即data[i]     public T get(int i) {         if(i<0 || i>length-1)             return null;         return (T)data[i];     }     //修改下標值為i的元素值,即修改data[i]     public boolean set(int i,T x) {         if(i<0 || i>length-1)             return false;         else{             data[i]=x;             return true;         }     }     //內部使用,提供資料元素的比較方法     private int compare(T a,T b) {         if(a instanceof Comparable && b instanceof Comparable) {             return ((Comparable) a).compareTo((Comparable) b);         }         else {             return ((String) a).compareTo((String) b);         }     }     //在begin和end之間查詢值為value的資料元素下標,使用順序查詢法     public int indexOf(int begin,int end,T value) {         //先查詢null值         if(value==null) {             for(int i=0;i<length;i++) {                 if(data[i]==null) {                     return i;                 }             }         }         //查詢有效值         else {             for(int i=0;i<length;i++) {                 if(compare((T)data[i],value)==0) {                     return i;                 }             }         }         return -1;     }     //內部使用,自動增加順序表容量     private void grow() {         int newSize=data.length+incrementSize;         data=Arrays.copyOf(data, newSize);     }     //在i位置插入資料元素x     public void add(int i, T x) {         //順序表存滿的時候自動增加順序表容量         if(length==data.length)             grow();         if(i<0) i=0;         if(i>length) {             i=length;         }         for(int j=length-1;j>=i;j--) {             data[j+1]=data[j];         }         data[i]=x;         length++;     }     //向表尾新增資料元素x     public void add(T x) {         if(length==data.length) {             grow();         }         data[length]=x;         length++;     }     //向表尾新增資料元素x,功能同上     public void append(T x) {         add(x);     }     //內部使用,以有序方式插入資料元素x     private void insertOrder(int end,T x) {         if(length==data.length) {             grow();         }         int k;         for(k=end-1;k>=0;k--) {             //當x小於data[k]時             if(compare(x,(T)data[k])<0) {                 data[k+1]=data[k];             }             else                 break;         }         data[k+1]=x;     }     //以有序方式向順序表中插入資料元素x     public void addSort(T x) {         insertOrder(length,x);         length++;     }     //對順序表排序     public void sort() {         for(int i=1;i<=length-1;i++) {             //呼叫插入函式insertOrder完成具體插入過程             insertOrder(i, (T)data[i]);         }     }     //刪除下標為i的元素,並返回被刪除的元素     public T remove(int i) {         if(i<0 || i>length-1)             throw new IndexOutOfBoundsException("下標越界 i="+i);         T olddata=(T)data[i];         for(int j=i;j<length-1;j++) {             data[j]=data[j+1];         }             length--;             data[length]=null;             return olddata;     }     //刪除值為value的元素     public T remove(T value) {         int i=indexOf(value);         return remove(i);     }     //清除整個列表     public void clear() {         for(int i=0;i<length;i++)             //將元素指向空指標,使用Java虛擬機器的辣雞回收機制自動處理             data[i]=null;         length=0;     }     //將順序錶轉化成字串,便於直接輸出順序表     public String toString() {         StringBuilder strbui=new StringBuilder();         strbui=strbui.append("(");         for(int i=0;i<length-1;i++) {             strbui.append(data[i].toString()+",");         }         strbui=strbui.append(data[length-1].toString()+")");         String string=new String(strbui);         strbui=null;         return string;     }     //將順序錶轉化成Object陣列,並返回     public Object[] toArray() {         return Arrays.copyOf(this.data, this.length);     }     //將順序錶轉換為型別為T的陣列(因為T是泛型,理論上可以代表任何變數型別)     public T[] toArray(T[] a) {         //當傳入的陣列a的長度小於順序表的長度時,建立一個與陣列a的執行時型別相同的陣列,a的長度可以是0         if(a.length<length) {             return (T[]) Arrays.copyOf(this.data, this.length,a.getClass());         }         //將data裡面的以第1個元素複製到a中第1個元素,以此往後加一,一共加到this.length-1的位置         System.arraycopy(this.data, 0, a, 0, this.length);         if(a.length>this.length) {             a[length]=null;         }         return a;     }     //建立一個迭代器類     class MyIterator implements Iterator<T>{         private int index=0;                   public boolean hasNext() {             return index!=length();         }         public T next() {             //用索引來獲取SeqList中下標為index的項             return get(index++);         }         public void remove() {             //未實現此方法         }     }     //返回一個迭代器物件     public Iterator<T> iterator(){         return new MyIterator();     } }

 

使用順序表示例

建立一個順序表並對以上類中函式進行實現檢驗,程式碼如下:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 /*  * created on April 16 21:33 2019  *  * @author:lhy  */ package DS; import java.util.Iterator;   import DS.SeqList;   /*  * SeqList API介紹  * setIncr(int inc): 設定順序表每次增加時增量的大小  * setCapacity(int newSize): 設定新的最大容量  * getCapacity(): 獲取陣列的最大容量  * size(): 獲取順序表已經存放資料的數量,同length()  * get(int i): 獲取順序表下標為i的元素值  * set(int i,T x): 修改下標值為i的元素值,即為修改data[i]  * indexOf(int begin,int end,T value): 在begin和end之間查詢值為value的元素下標值,使用順序查詢法  * indexOf(T o): 在整個線性表中查詢元素o的下標值  * indexOf(int begin,T o): 在下標為begin到末尾的線性表中查詢元素o的下標值  * add(int i,T x): 在i位置插入資料元素x  * add(T x): 在表尾新增資料元素x  * append(T x): 在表尾新增資料元素x  * addSort(T x): 以有序方式向順序表中插入資料元素x  * sort(): 對順序表從小到大排序  * remove(int i): 刪除下標為i的元素,並返回被刪除的元素  * remove(T value): 刪除值為value的元素,並返回被刪除的元素  * clear(): 清除整個順序表  * toString(): 將順序錶轉化成字串,便於直接輸出順序表  * toArray(): 將順序錶轉化成Object陣列,並返回  * toArray(T[] a): 將順序錶轉換為型別為E的陣列  * iterator(): 返回一個迭代物件  * length(): 獲取線性表長度  * isEmpty(): 判斷線性表是否為空  */   public class Test_SeqList {       public static void main(String[] args) {         //new一個順序表物件         SeqList<Integer> seqList= new SeqList<Integer>(16);         System.out.println("順序表是否為空:"+seqList.isEmpty());         //隨機初始化順序表         for(int i=0;i<6;i++)         seqList.add((int)(1+Math.random()*(1000)));         //建立一個順序表迭代器,並輸出順序表         Iterator<Integer> seq_iterator=seqList.iterator();         System.out.print("使用迭代器輸出整個順序表:");         for(int i=0;i<6;i++)             System.out.print(seq_iterator.next()+" ");         System.out.print("\n");         System.out.println("初始化後,順序表是否為空:"+seqList.isEmpty());         System.out.println("獲取順序表容量:"+seqList.getCapacity());         System.out.println("刪除下標為2的元素:"+seqList.remove(2));         System.out.println("刪除下標為2的元素之後順序表為:"+seqList.toString());         seqList.append(78);         System.out.println("順序表末尾加入資料78之後順序表為:"+seqList.toString());         seqList.sort();         System.out.println("將順序表從大到小排序後順序表為:"+seqList.toString());         seqList.add(1,233);         System.out.println("在下標為1的位置插入元素233之後,順序表為:"+seqList.toString());         System.out.println("獲取下標為length-1(最後一個元素)的位置的元素為:"+seqList.get(seqList.length()-1));         seqList.add(998);         System.out.println("順序表末尾加入資料998之後順序表為:"+seqList.toString());         seqList.sort();         seqList.addSort(555);         System.out.println("對順序表排序後,有序插入資料555順序表為:"+seqList.toString());         System.out.println("查詢排序後的元素555所在的位置:"+seqList.indexOf(0,seqList.length(),555));         seqList.setCapacity(32);         System.out.println("將順序表最大容量變成32之後,最大容量為"+seqList.getCapacity());         Integer num=new Integer(555);         System.out.println("刪除元素555:"+seqList.remove(num));         System.out.println("刪除元素555之後順序表為:"+seqList.toString());                   //將seqList順序錶轉化成Integer[]陣列,使用toArray(),這個函式返回的是Object[]型別的陣列         Integer[] nums=new Integer[seqList.length()];         //TIPS:因為Integer[]不是Object[]的子類,所以不能直接強制型別轉換(Integer是Object的子類)         Object[] nums_object=seqList.toArray();         for(int i=0;i<nums.length;i++)             nums[i]=(Integer)nums_object[i];         System.out.print("輸出轉化後的陣列num:");         for(int j=0;j<nums.length;j++) {             System.out.print(nums[j]+" ");         }         System.out.print("\n");                   //將seqList順序錶轉化成Integer[]陣列,使用toArray(T[] a)),這個函式返回的是T型別的陣列         Integer[] nums2=new Integer[seqList.length()];         nums2=seqList.toArray(nums2);         System.out.print("輸出轉化後的陣列num2:");         for(int j=0;j<nums2.length;j++) {             System.out.print(nums2[j]+" ");         }                   System.out.print("\n");         System.out.println("使用clear函式清空整個順序表");         seqList.clear();         System.out.println("順序表現在是否為空:"+seqList.isEmpty());     }   }

執行結果:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 順序表是否為空:true 使用迭代器輸出整個順序表:700 889 70 218 510 319 初始化後,順序表是否為空:false 獲取順序表容量:16 刪除下標為2的元素:70 刪除下標為2的元素之後順序表為:(700,889,218,510,319) 順序表末尾加入資料78之後順序表為:(700,889,218,510,319,78) 將順序表從大到小排序後順序表為:(78,218,319,510,700,889) 在下標為1的位置插入元素233之後,順序表為:(78,233,218,319,510,700,889) 獲取下標為length-1(最後一個元素)的位置的元素為:889 順序表末尾加入資料998之後順序表為:(78,233,218,319,510,700,889,998) 對順序表排序後,有序插入資料555順序表為:(78,218,233,319,510,555,700,889,998) 查詢排序後的元素555所在的位置:5 將順序表最大容量變成32之後,最大容量為32 刪除元素555555 刪除元素555之後順序表為:(78,218,233,319,510,700,889,998) 輸出轉化後的陣列num:78 218 233 319 510 700 889 998 輸出轉化後的陣列num2:78 218 233 319 510 700 889 998 使用clear函式清空整個順序表 順序表現在是否為空:true