循環鏈表的應用之約瑟夫環問題以及線性表總結之順序表與鏈表的比較
1.1問題說明
問題描述:編號爲1,2,···,n的n個人圍坐在一圓桌旁,每人持有一個正整數的密碼。從第一個人開始報數,報到一個預先約定的正整數m時,停止報數,報m的人退席,下一個人又重新從1開始報數,依此重複,直至所有的人都退席。編一程序輸出他們退席的編號序列。例如,設m=20,n=7,7個人的密碼依次是3,1,7,2,4,8,4,則退席的人的編號依次爲6,1,7,5,3,2,4。
基本要求:用不帶表頭結點的循環單鏈表表示圍成圓圈的n個人;要求建立此循環單鏈表;某人離席相當於刪除一個結點,要正確設置程序中循環終止的條件和刪除結點時指針的修改變化。
1.2代碼實現
#include #include #define NULL 0 typedef int ElemType; typedef struct LNode{ ElemType data; ElemType sequence; LNode *next; }LNode,*LinkList;
//創建一個不帶頭節點的循環單向鏈表 void createCircularList(LinkList &L, int n){ printf("依次輸入數據元素:\n"); //輸入第一個元素,即頭節點 LinkList head = (LinkList)malloc(sizeof(LNode)); head->sequence = 1; head->next = NULL; scanf("%d", &head->data); L = head; LinkList p = head; int i = 2; while(i <= n){ LinkList s = (LinkList)malloc(sizeof(LNode)); s->sequence = i; s->next = NULL; scanf("%d", &s->data); p->next = s; p = s; i++; } p->next = L; }
//打印輸出單項循環鏈表 void printCircularList(LinkList L){ printf("打印單項循環鏈表:"); LinkList head = L; LinkList p = L->next; printf("%d ",head->data); while(p!=head){ printf("%d ", p->data); p = p->next; } printf("\n"); }
//約瑟夫環的實現 void josephRing(LinkList L, int m, int n){ int *outNum = new int[n], num=0;//按退出順序記錄編號 int count = 1;//報數 LinkList p = L, q = L; while(p->next!=p){ if(count%m == 0){ q->next = p->next; outNum[num] = p->sequence; num++; free(p); p = q->next; count = 1; }else{ q = p; p = p->next; count++; } } outNum[num] = p->sequence; printf("退出的編號順序是:"); for(int i = 0; i < n; i++){ printf("%d ", outNum[i]); } printf("\n"); }
//實例:設m=20,n=7,7個人的密碼依次是3,1,7,2,4,8,4, //則退席的人的編號依次爲6,1,7,5,3,2,4。 void main(){ LinkList L; createCircularList(L, 7); printCircularList(L); josephRing(L, 20, 7); }
2.線性表總結之順序表與鏈表的比較
線性表有兩種存儲結構:順序表和鏈表,通過對它們的討論可知它們各有優缺點。
順序存儲有三個優點:
(1) 方法簡單,各種高級語言中都有數組,容易實現。
(2) 不用爲表示結點間的邏輯關係而增加額外的存儲開銷。
(3) 順序表具有按元素序號隨機訪問的特點。
但它也有兩個缺點:
(1) 在順序表中做插入刪除操作時,平均移動大約表中一半的元素,因此對n較大的順序表效率低。
(2) 需要預先分配足夠大的存儲空間,估計過大,可能會導致順序表後部大量閒置;預先分配過小,又會造成溢出。
鏈表的優缺點恰好與順序表相反。
在實際中怎樣選取存儲結構呢?通常有以下幾點考慮:
1.基於存儲的考慮
順序表的存儲空間是靜態分配的,在程序執行之前必須明確規定它的存儲規模,也就是說事先對“MAXSIZE(n0)"要有合適的設定,過大造成浪費,過小造成溢出。可見對線性表的長度或存儲規模難以估計時,不宜採用順序表;鏈表不用事先估計存儲規模,但鏈表的存儲密度較低,存儲密度是指一個結點中數據元素所佔的存儲單元和整個結點所佔的存儲單元之比。顯然鏈式存儲結構的存儲密度是小於1的。
2.基於運算的考慮
在順序表中按序號訪問ai的時間性能時O(1),而鏈表中按序號訪問的時間性能O(n),所以如果經常做的運算是按序號訪問數據元素,顯然順序表優於鏈表;而在順序表中做插入、刪除時平均移動表中一半的元素,當數據元素的信息量較大且表較長時,這一點是不應忽視的;在鏈表中作插入、刪除,雖然也要找插入位置,但操作主要是比較操作,從這個角度考慮顯然後者優於前者。
3.基於環境的考慮
順序表容易實現,任何高級語言中都有數組類型,鏈表的操作是基於指針的,相對來講前者簡單些,也是用戶考慮的一個因素。
總之,兩中存儲結構各有長短,選擇那一種由實際問題中的主要因素決定。通常“較穩定”的線性表選擇順序存儲,而頻繁做插入刪除的即動態性較強的線性表宜選擇鏈式存儲。
小結
線性表是一種最基本,最常用的數據結構。線性表有兩種存儲結構----順序表和鏈表,以及在這兩種存儲結構上實現的基本運算。
順序表是用數組實現的,鏈表是用指針或遊標實現的。用指針來實現的鏈表,因爲它的結點是動態分配的,故稱之爲動態鏈表;用遊標模擬指針實現的鏈表,由於其結點空間是靜態分配的,所以稱之爲靜態鏈表。這兩種鏈表又可按鏈接形式的不同,區分爲單鏈表,雙鏈表和循環鏈表。
在實際應用中,對線性表採用哪種存儲結構,要視實際問題的要求而定,主要考慮求解算法的時間複雜度和空間複雜度。因此,建議熟練掌握在順序表和鏈表上實現的各種基本運算及其時間,空間特性。
最後分享些循環鏈表及線性表的應用方面的資料
http://www.makeru.com.cn/course/details/1902?s=45051