1、單向循環鏈表:java
一、概念:node
單向循環鏈表是單鏈表的另外一種形式,其結構特色是鏈表中最後一個結點的指針再也不是結束標記,而是指向整個鏈表的第一個結點,從而使單鏈表造成一個環。算法
和單鏈表相比,循環單鏈表的長處是從鏈尾到鏈頭比較方便。當要處理的數據元素序列具備環型結構特色時,適合於採用循環單鏈表。數組
和單鏈表相同,循環單鏈表也有帶頭結點結構和不帶頭結點結構兩種,帶頭結點的循環單鏈表實現插入和刪除操做時,算法實現較爲方便。dom
帶頭結點的循環單鏈表的操做實現方法和帶頭結點的單鏈表的操做實現方法類同,差異僅在於:ide
(1)在構造函數中,要加一條head.next = head 語句,把初始時的帶頭結點的循環單鏈表設計成上圖中(a)所示的狀態。函數
(2)在index(i)成員函數中,把循環結束判斷條件current != null改成current != head。this
二、單鏈表的代碼實現:spa
先回顧上一篇文章,定位到第三段「3、單項鍊表的代碼實現」,咱們是須要修改這段裏面的(3)LinkList.java代碼,(1)和(2)的代碼不變。設計
(3)LinkList.java:單項循環鏈表類:(核心代碼)
1 //單向循環鏈表類 2 public class CycleLinkList implements List { 3 4 Node head; //頭指針 5 Node current;//當前結點對象 6 int size;//結點個數 7 8 //初始化一個空鏈表 9 public CycleLinkList() 10 { 11 //初始化頭結點,讓頭指針指向頭結點。而且讓當前結點對象等於頭結點。 12 this.head = current = new Node(null); 13 this.size =0;//單向鏈表,初始長度爲零。 14 this.head.next = this.head; 15 } 16 17 //定位函數,實現當前操做對象的前一個結點,也就是讓當前結點對象定位到要操做結點的前一個結點。 18 //好比咱們要在a2這個節點以前進行插入操做,那就先要把當前節點對象定位到a1這個節點,而後修改a1節點的指針域 19 public void index(int index) throws Exception 20 { 21 if(index <-1 || index > size -1) 22 { 23 throw new Exception("參數錯誤!"); 24 } 25 //說明在頭結點以後操做。 26 if(index==-1) //由於第一個數據元素結點的下標是0,那麼頭結點的下標天然就是-1了。 27 return; 28 current = head.next; 29 int j=0;//循環變量 30 while(current != head&&j<index) 31 { 32 current = current.next; 33 j++; 34 } 35 36 } 37 38 @Override 39 public void delete(int index) throws Exception { 40 // TODO Auto-generated method stub 41 //判斷鏈表是否爲空 42 if(isEmpty()) 43 { 44 throw new Exception("鏈表爲空,沒法刪除!"); 45 } 46 if(index <0 ||index >size) 47 { 48 throw new Exception("參數錯誤!"); 49 } 50 index(index-1);//定位到要操做結點的前一個結點對象。 51 current.setNext(current.next.next); 52 size--; 53 } 54 55 @Override 56 public Object get(int index) throws Exception { 57 // TODO Auto-generated method stub 58 if(index <-1 || index >size-1) 59 { 60 throw new Exception("參數非法!"); 61 } 62 index(index); 63 64 return current.getElement(); 65 } 66 67 @Override 68 public void insert(int index, Object obj) throws Exception { 69 // TODO Auto-generated method stub 70 if(index <0 ||index >size) 71 { 72 throw new Exception("參數錯誤!"); 73 } 74 index(index-1);//定位到要操做結點的前一個結點對象。 75 current.setNext(new Node(obj,current.next)); 76 size++; 77 } 78 79 @Override 80 public boolean isEmpty() { 81 // TODO Auto-generated method stub 82 return size==0; 83 } 84 @Override 85 public int size() { 86 // TODO Auto-generated method stub 87 return this.size; 88 } 89 90 91 }
14行是添加的代碼,30行是修改的代碼。
三、單項循環鏈表的應用舉例:
編寫擊鼓傳花小遊戲。
遊戲規則:N我的圍成一個圈,從第一我的開始傳花,當數到M時,該人退出遊戲,直到剩下最後一我的。
代碼實現:
(4)Game.java:
1 //遊戲類 2 public class Game { 3 4 //單向循環鏈表 5 CycleLinkList list = new CycleLinkList(); 6 //總人數 7 int num; 8 //數到幾退出 9 int key; 10 11 //遊戲初始化方法 12 public Game(int num,int key) 13 { 14 this.num = num; 15 this.key = key; 16 } 17 18 public void play() throws Exception 19 { 20 for(int i=0;i<num;i++) 21 { 22 list.insert(i, i); 23 } 24 25 System.out.println("\n-------遊戲開始以前---------\n"); 26 for(int i=0;i<list.size;i++) 27 { 28 System.out.print(list.get(i)+" "); 29 } 30 System.out.println("\n-------遊戲開始---------\n"); 31 int iCount=num; //開始等於總人數num 32 int j=0; //累加器,計算是否能被key整除。 33 34 Node node = list.head; 35 while(iCount!=1) 36 { 37 if(node.getElement()!=null&& Integer.parseInt(node.getElement().toString())!=-1) 38 { 39 j++; 40 if(j%key==0) 41 { 42 node.setElement(-1); 43 iCount--; 44 System.out.println(); 45 for(int i=0;i<list.size;i++) 46 { 47 System.out.print(list.get(i)+" "); 48 } 49 } 50 } 51 node = node.next; 52 } 53 System.out.println("\n-------遊戲結束---------\n"); 54 for(int i=0;i<list.size;i++) 55 { 56 System.out.print(list.get(i)+" "); 57 } 58 } 59 60 }
(5)Test.java:
1 public class Test { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) throws Exception { 7 // TODO Auto-generated method stub 8 /* 9 CycleLinkList list = new CycleLinkList(); 10 for(int i=0;i<10;i++) 11 { 12 int temp = ((int)(Math.random()*100))%100; 13 list.insert(i, temp); 14 System.out.print(temp+" "); 15 } 16 list.delete(4); 17 System.out.println("\n------刪除第五個元素以後-------"); 18 for(int i=0;i<list.size;i++) 19 { 20 System.out.print(list.get(i)+" "); 21 }*/ 22 23 Game game = new Game(10,3); 24 game.play(); 25 26 } 27 }
2、雙向循環鏈表:
雙向鏈表:
雙向鏈表是每一個結點除後繼指針外還有一個前驅指針。和單鏈表類同,雙向鏈表也有帶頭結點結構和不帶頭結點結構兩種,帶頭結點的雙向鏈表更爲經常使用;另外,雙向鏈表也能夠有循環和非循環兩種結構,循環結構的雙向鏈表更爲經常使用。
雙向循環鏈表:
在雙向鏈表中,每一個結點包括三個域,分別是element域、next域和prior域,其中element域爲數據元素域,next域爲指向後繼結點的對象引用,prior域爲指向前驅結點的對象引用。下圖爲雙向鏈表結點的圖示結構:
以下圖是帶頭結點的雙向循環鏈表的圖示結構。雙向循環鏈表的next和prior各自構成本身的單向循環鏈表:
在雙向鏈表中,有以下關係:設對象引用p表示雙向鏈表中的第i個結點,則p.next表示第i+1個結點,p.next.prior仍表示第i個結點,即p.next.prior == p;一樣地,p.prior表示第i-1個結點,p.prior.next仍表示第i個結點,即p.prior.next == p。下圖是雙向鏈表上述關係的圖示:
雙向循環鏈表的插入過程:
下圖中的指針p表示要插入結點的位置,s表示要插入的結點,①、②、③、④表示實現插入過程的步驟:
循環雙向鏈表的刪除過程:
下圖中的指針p表示要插入結點的位置,①、②表示實現刪除過程的步驟:
二、雙向循環鏈表的代碼實現:
(1)List.java:
1 //線性表接口 2 public interface List { 3 //得到線性表長度 4 public int size(); 5 6 //判斷線性表是否爲空 7 public boolean isEmpty(); 8 9 //插入元素 10 public void insert(int index, Object obj) throws Exception; 11 12 //刪除元素 13 public void delete(int index) throws Exception; 14 15 //獲取指定位置的元素 16 public Object get(int index) throws Exception; 17 }
(2)Node.java:
1 //結點類 2 public class Node { 3 4 Object element; //數據域 5 Node next; //後繼指針域 6 Node prior; //前驅指針域 7 8 //頭結點的構造方法 9 public Node(Node nextval) { 10 this.next = nextval; 11 } 12 13 //非頭結點的構造方法 14 public Node(Object obj, Node nextval) { 15 this.element = obj; 16 this.next = nextval; 17 } 18 19 //得到當前結點的後繼結點 20 public Node getNext() { 21 return this.next; 22 } 23 24 //得到當前結點的前驅結點 25 public Node getPrior() { 26 return this.prior; 27 } 28 29 //得到當前的數據域的值 30 public Object getElement() { 31 return this.element; 32 } 33 34 //設置當前結點的後繼指針域 35 public void setNext(Node nextval) { 36 this.next = nextval; 37 } 38 39 //設置當前結點的前驅指針域 40 public void setPrior(Node priorval) { 41 this.prior = priorval; 42 } 43 44 //設置當前結點的數據域 45 public void setElement(Object obj) { 46 this.element = obj; 47 } 48 49 public String toString() { 50 return this.element.toString(); 51 } 52 }
(3)DoubleCycleLinkList.java:
1 //單向鏈表類 2 public class DoubleCycleLinkList implements List { 3 4 Node head; //頭指針 5 Node current;//當前結點對象 6 int size;//結點個數 7 8 //初始化一個空鏈表 9 public DoubleCycleLinkList() { 10 //初始化頭結點,讓頭指針指向頭結點。而且讓當前結點對象等於頭結點。 11 this.head = current = new Node(null); 12 this.size = 0;//單向鏈表,初始長度爲零。 13 this.head.next = head; 14 this.head.prior = head; 15 } 16 17 //定位函數,實現當前操做對象的前一個結點,也就是讓當前結點對象定位到要操做結點的前一個結點。 18 public void index(int index) throws Exception { 19 if (index < -1 || index > size - 1) { 20 throw new Exception("參數錯誤!"); 21 } 22 //說明在頭結點以後操做。 23 if (index == -1) 24 return; 25 current = head.next; 26 int j = 0;//循環變量 27 while (current != head && j < index) { 28 current = current.next; 29 j++; 30 } 31 32 } 33 34 @Override 35 public void delete(int index) throws Exception { 36 // TODO Auto-generated method stub 37 //判斷鏈表是否爲空 38 if (isEmpty()) { 39 throw new Exception("鏈表爲空,沒法刪除!"); 40 } 41 if (index < 0 || index > size) { 42 throw new Exception("參數錯誤!"); 43 } 44 index(index - 1);//定位到要操做結點的前一個結點對象。 45 current.setNext(current.next.next); 46 current.next.setPrior(current); 47 size--; 48 } 49 50 @Override 51 public Object get(int index) throws Exception { 52 // TODO Auto-generated method stub 53 if (index < -1 || index > size - 1) { 54 throw new Exception("參數非法!"); 55 } 56 index(index); 57 58 return current.getElement(); 59 } 60 61 @Override 62 public void insert(int index, Object obj) throws Exception { 63 // TODO Auto-generated method stub 64 if (index < 0 || index > size) { 65 throw new Exception("參數錯誤!"); 66 } 67 index(index - 1);//定位到要操做結點的前一個結點對象。 68 current.setNext(new Node(obj, current.next)); 69 current.next.setPrior(current); 70 current.next.next.setPrior(current.next); 71 72 size++; 73 } 74 75 @Override 76 public boolean isEmpty() { 77 // TODO Auto-generated method stub 78 return size == 0; 79 } 80 81 @Override 82 public int size() { 83 // TODO Auto-generated method stub 84 return this.size; 85 } 86 87 88 }
(4)Test.java:
1 public class Test { 2 3 /** 4 * @param args 5 */ 6 public static void main(String[] args) throws Exception { 7 // TODO Auto-generated method stub 8 DoubleCycleLinkList list = new DoubleCycleLinkList(); 9 for (int i = 0; i < 10; i++) { 10 int temp = ((int) (Math.random() * 100)) % 100; 11 list.insert(i, temp); 12 System.out.print(temp + " "); 13 } 14 list.delete(4); 15 System.out.println("\n------刪除第五個元素以後-------"); 16 for (int i = 0; i < list.size; i++) { 17 System.out.print(list.get(i) + " "); 18 } 19 } 20 21 }
運行效果:
3、仿真鏈表:
在鏈式存儲結構中,咱們實現數據元素之間的次序關係依靠指針。咱們也能夠用數組來構造仿真鏈表。方法是在數組中增長一個(或兩個)int類型的變量域,這些變量用來表示後一個(或前一個)數據元素在數組中的下標。咱們把這些int類型變量構造的指針稱爲仿真指針。這樣,就能夠用仿真指針構造仿真的單鏈表(或仿真的雙向鏈表)。