標籤:表示 模擬 自己的 tar 狀態 插入 說明 i+1 ide
一、單向迴圈鏈表:
1、概念:
單向迴圈鏈表是單鏈表的另一種形式,其結構特點是鏈表中最後一個結點的指標不再是結束標記,而是指向整個鏈表的第一個結點,從而使單鏈表形成一個環。
和單鏈表相比,迴圈單鏈表的長處是從鏈尾到鏈頭比較方便。當要處理的資料元素序列具有環型結構特點時,適合於採用迴圈單鏈表。
和單鏈表相同,迴圈單鏈表也有帶頭結點結構和不帶頭結點結構兩種,帶頭結點的迴圈單鏈表實現插入和刪除操作時,演算法實現較為方便。
帶頭結點的迴圈單鏈表的操作實現方法和帶頭結點的單鏈表的操作實現方法類同,差別僅在於:
(1)在建構函式中,要加一條head.next = head 語句,把初始時的帶頭結點的迴圈單鏈表設計成中(a)所示的狀態。
(2)在index(i)成員函數中,把迴圈結束判斷條件current != null改為current != head。
2、單鏈表的代碼實現:
先回顧上一篇文章,定位到第三段“三、單項鏈表的代碼實現”,我們是需要修改這段裡面的(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 Exception20 {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 @Override39 public void delete(int index) throws Exception {40 // TODO Auto-generated method stub41 //判斷鏈表是否為空白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 @Override56 public Object get(int index) throws Exception {57 // TODO Auto-generated method stub58 if(index <-1 || index >size-1)59 {60 throw new Exception("參數非法!");61 }62 index(index);63 64 return current.getElement();65 }66 67 @Override68 public void insert(int index, Object obj) throws Exception {69 // TODO Auto-generated method stub70 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 @Override80 public boolean isEmpty() {81 // TODO Auto-generated method stub82 return size==0;83 }84 @Override85 public int size() {86 // TODO Auto-generated method stub87 return this.size;88 }89 90 91 }
14行是添加的代碼,30行是修改的代碼。
3、單項迴圈鏈表的應用舉例:
編寫擊鼓傳花小遊戲。
遊戲規則: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 Exception19 {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; //開始等於總人數num32 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 }
二、雙向迴圈鏈表:
雙向鏈表:
雙向鏈表是每個結點除後繼指標外還有一個前驅指標。和單鏈表類同,雙向鏈表也有帶頭結點結構和不帶頭結點結構兩種,帶頭結點的雙向鏈表更為常用;另外,雙向鏈表也可以有迴圈和非迴圈兩種結構,迴圈結構的雙向鏈表更為常用。
雙向迴圈鏈表:
在雙向鏈表中,每個結點包括三個域,分別是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表示要插入結點的位置,①、②表示實現刪除過程的步驟:
2、雙向迴圈鏈表的代碼實現:
(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 @Override35 public void delete(int index) throws Exception {36 // TODO Auto-generated method stub37 //判斷鏈表是否為空白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 @Override51 public Object get(int index) throws Exception {52 // TODO Auto-generated method stub53 if (index < -1 || index > size - 1) {54 throw new Exception("參數非法!");55 }56 index(index);57 58 return current.getElement();59 }60 61 @Override62 public void insert(int index, Object obj) throws Exception {63 // TODO Auto-generated method stub64 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 @Override76 public boolean isEmpty() {77 // TODO Auto-generated method stub78 return size == 0;79 }80 81 @Override82 public int size() {83 // TODO Auto-generated method stub84 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 }
運行效果:
三、模擬鏈表:
在鏈式儲存結構中,我們實現資料元素之間的次序關係依靠指標。我們也可以用數組來構造模擬鏈表。方法是在數組中增加一個(或兩個)int類型的變數域,這些變數用來表示後一個(或前一個)資料元素在數組中的下標。我們把這些int類型變數構造的指標稱為模擬指標。這樣,就可以用模擬指標構造模擬的單鏈表(或模擬的雙向鏈表)。
資料結構Java實現04----迴圈鏈表、模擬鏈表