最近學了單鏈表,遇到了一些問題 ,和大家分享一下!
首先看一下帶頭指針的單鏈表示意圖:
從中可以看到鏈表的每一個節點(除了尾指針)都有一個指針指向下一個節點(頭指針只有只保存了該鏈表的地址),尾指針指向空。
所以我們要對鏈表中的某個節點進行操作的話,基本上要使用到該節點的前驅節點和后驅節點。(節點2的前驅節點是節點1,節點2的后驅節點是節點3;)
單鏈表的排序關鍵在交換,交換有兩種方式 一種是節點值的交換,另一種是節點的交換。
先講第一種節點值交換法:
1 #include <stdio.h> 2 #include <stdlib.h> 3 //定義STUDENT類型變量 包括了學號、分數 4 typedef struct student { 5 int number; 6 int score; 7 struct student *pNext; 8 }STUDENT; 9 10 STUDENT *Create(int n) { 11 STUDENT *pHead, *pEnd,*pNew=NULL; 12 int i; 13 pHead=pEnd= (STUDENT*)malloc(sizeof(STUDENT)); 14 for (i = 0; i < n; i++) 15 { 16 pNew = (STUDENT*)malloc(sizeof(STUDENT)); 17 scanf("%d", &pNew->number); 18 scanf("%d", &pNew->score); 19 pNew->pNext = NULL; 20 pEnd->pNext = pNew; 21 pEnd = pNew; 22 23 } 24 return pHead; 25 } 26 //輸出鏈表 27 void print(STUDENT *p1) { 28 while (p1->pNext != NULL) { 29 p1 = p1->pNext; 30 printf("%d %d\n", p1->number, p1->score); 31 } 32 } 33 //冒泡排序 節點值交換法 34 void Sort(STUDENT *head) { 35 STUDENT *p, *q; 36 for (p = head->pNext; p != NULL; p = p->pNext) 37 for (q = p->pNext; q != NULL; q = q->pNext) 38 if (p->number > q->number)//根據學號從小到大排序 39 { 40 int t1 = p->number; p->number = q->number; q->number = t1; 41 int t2 = p->score; p->score = q->score; q->score = t2; 42 } 43 44 } 45 46 int main() { 47 int n; 48 STUDENT *p; 49 scanf("%d", &n); 50 p= Create(n); 51 Sort(p); 52 print(p); 53 }
根據算法不難看出節點值交換法適用於節點中變量成員較少的情況下,但是實際問題往往是復雜的。
比如節點中增加學生的姓名等等,那么想通過交換節點值的方法是比較繁瑣的。所以我就想能不能想通過交換任意兩個節點的位置排序呢?
之后寫下了如下代碼:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 //定義STUDENT類型變量 包括學號、姓名、分數 5 typedef struct student { 6 unsigned number; 7 char name[50]; 8 unsigned score; 9 struct student *pNext; 10 }STUDENT; 11 //尋找前驅節點 12 STUDENT *Find_before(STUDENT* phead, STUDENT* p) 13 { 14 if (!p) return NULL; 15 STUDENT *pbefore = phead; 16 while (pbefore) 17 { 18 if (pbefore->pNext == p) 19 return pbefore; 20 pbefore = pbefore->pNext; 21 } 22 return NULL; 23 } 24 //冒泡排序 按照分數高低排序 交換任意的滿足條件的節點 25 //head 為鏈表的頭地址 26 void Sort(STUDENT *head) { 27 STUDENT *p, *pbefore, *q, *qbefore, *t1, *t2; 28 STUDENT *phead = head; 29 for (p = head->pNext; p != NULL; p = p->pNext) 30 { 31 pbefore = Find_before(phead, p); 32 for (q = p->pNext; q != NULL; q = q->pNext) 33 { 34 qbefore = Find_before(phead, q); 35 //當p q節點不相鄰 36 if (p->score < q->score && p->pNext != q) 37 { 38 t1 = pbefore->pNext; //保存p節點地址 39 pbefore->pNext = qbefore->pNext; //把q節點賦值給p節點 40 qbefore->pNext = t1; //p節點賦值給q節點 41 t2 = q->pNext; //保存q的后驅節點 42 q->pNext = p->pNext; //把p的后驅節點賦值給q的后驅節點 43 p->pNext = t2; //把q的后驅節點賦值給p的后驅節點 44 } 45 //當p q節點相鄰 46 else if (p->score < q->score && p->pNext == q) 47 { 48 t1 = pbefore->pNext; 49 pbefore->pNext = p->pNext; 50 p->pNext = t1; 51 t1 = q->pNext; 52 } 53 54 } 55 56 } 57 58 }
上面的代碼看似沒有問題 但卻忽略了一個問題--交換之后的p,q 將for語句的循環破壞了! 我們來看一下p q不相鄰情況下的交換圖:
如果按照for語句中執行語句q=q->pNext ,q更新后為節點3,而不是我們想要的節點5。所以這種交換任意節點排序的方法是錯誤的
之后我又換了個思路:(假設按分數從高到低為鏈表節點排序)在鏈表a中找到最高分節點q,然后將q節點接在b鏈表(空鏈表)的頭指針后面,
再通過q的前后驅動節點將q從a鏈表中剔除,接下來又在a鏈表中尋找最高分節點q',如此循環 直到a鏈表為空。
代碼如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 //創建STUDENT類型變量 包括學號、姓名、成績 5 typedef struct student { 6 int number; 7 char name[50]; 8 int score; 9 struct student *pNext; 10 }STUDENT; 11 //創建鏈表 12 STUDENT *Create(int n) { 13 STUDENT *pHead, *pEnd, *pNew = NULL; 14 int i; pHead = pEnd = (STUDENT*)malloc(sizeof(STUDENT)); 16 for (i = 0; i < n; i++) 17 { 18 pNew = (STUDENT*)malloc(sizeof(STUDENT)); 19 scanf("%d%s%d", &pNew->number, &pNew->name, &pNew->score); 20 21 pNew->pNext = NULL; 22 pEnd->pNext = pNew; 23 pEnd = pNew; 24 25 } 26 return pHead; 27 } 28 //尋找前驅節點 29 STUDENT *Find_before(STUDENT* phead, STUDENT* p) 30 { 31 if (!p) return NULL; 32 STUDENT *pbefore = phead; 33 while (pbefore) 34 { 35 if (pbefore->pNext == p) 36 return pbefore; 37 pbefore = pbefore->pNext; 38 } 39 return NULL; 40 } 41 42 STUDENT *Sort(STUDENT *head) { 43 STUDENT *pHead,*pEnd,*q=NULL,*qbefore=NULL,*p=NULL; 44 int maxscore; 45 pHead=pEnd = (STUDENT*)malloc(sizeof(STUDENT)); 46 while (head->pNext != NULL) 47 { 48 maxscore = head->pNext->score; 49 q = p = head->pNext; 50 //尋找最高分節點p 51 while (p->pNext!=NULL) 52 { 53 if(maxscore<p->pNext->score) 54 { 55 maxscore = p->pNext->score; q = p->pNext; 56 } 57 p = p->pNext; 58 } 59 60 qbefore = Find_before(head,q); //尋找q節點的前驅節點 61 qbefore->pNext = q->pNext; //將q的前驅節點指向q的后驅節點 從而將q節點從a鏈表中剔除 62 63 pEnd->pNext = q; //將頭指針指向q 64 q->pNext = NULL; //q節點指向空 65 pEnd = q; //更新尾節點 66 67 68 } 69 free(head);//釋放head鏈表頭節點 70 return pHead; 71 } 72 void print(STUDENT *q) { 73 while (q->pNext != NULL) 74 { 75 q = q->pNext; printf("%s\n", q->name); 76 } 77 free(q);//釋放使用完的鏈表 78 } 79 int main() { 80 int n, i = 1; STUDENT *p,*q; 81 scanf("%d", &n); 82 p = Create(n); 83 q=Sort(p); 84 print(q); 85 }
如果有什么錯誤,請指正! 萬分感謝!