1. 程式人生 > >關於單鏈表的排序問題

關於單鏈表的排序問題

  最近學了單鏈表,遇到了一些問題 ,和大家分享一下!

首先看一下帶頭指標的單鏈表示意圖: 

從中可以看到連結串列的每一個節點(除了尾指標)都有一個指標指向下一個節點(頭指標只有只儲存了該連結串列的地址),尾指標指向空。

所以我們要對連結串列中的某個節點進行操作的話,基本上要使用到該節點的前驅節點和後驅節點。(節點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; char str[100][50];
15     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, &str[i], &pNew->score);
20         strcpy(pNew->name, str[i]);//只有字串初始化時才能使用“=”賦值
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         pEnd->pNext = q;    //將頭指標指向q
60         q->pNext = NULL;    //q節點指向空
61         pEnd = q;            //更新尾節點
62         qbefore = Find_before(head,q);  //尋找q節點的前驅節點
63         qbefore->pNext = q->pNext; //將q的前驅節點指向q的後驅節點 從而將q節點從a連結串列中剔除
64     }
65     free(head);//釋放head連結串列頭節點
66     return pHead;
67 }
68 void print(STUDENT *q) {
69     while (q->pNext != NULL)
70     {
71         q = q->pNext; printf("%s\n", q->name);
72     }
73     free(q);//釋放使用完的連結串列
74 }
75 int main() {
76     int n, i = 1; STUDENT *p,*q;
77     scanf("%d", &n);
78     p = Create(n);
79     q=Sort(p);
80     print(q);
81 }

        如果有什麼錯誤,請指正! 萬分感謝!