#include<stdio.h> #include<malloc.h> #define LEN sizeof(struct Student) struct Student //結構體聲明 { long num; int score; struct Student* next; }; int n; struct Student* creat() //創建單向鏈表 { struct Student *head=NULL, *p_before, *p_later; p_before = p_later = (struct Student*) malloc(LEN); scanf_s("%ld%d", &p_before->num, &p_before->score); while (p_before->num!=0) { n++; if (n == 1) head = p_before; else p_later->next = p_before; p_later = p_before; p_before = (struct Student*) malloc(LEN); scanf_s("%ld%d", &p_before->num, &p_before->score); } p_later->next = NULL; free(p_before); return head; } struct Student* sort(struct Student* list) //冒泡排序,當初寫的是內容交換而不是指針交換,我知道這不是好的做法,但日子一久,當下沒時間和熱情改了,大家原諒, { //等有時間了一定改 struct Student *p, *q; int temp1,i; long temp2; for (p = list, i =1; i < n;i++,p=p->next) for (q = p->next;q!= NULL;q=q->next) if (p->score < q->score) { temp1 = p->score; p->score = q->score; q->score = temp1; temp2 = p->num; p->num = q->num; q->num = temp2; } return list; } struct Student* sort1(struct Student* h) //插入排序(下邊這堆注釋是當初寫完代碼后又分析時加的,這里必須承認,我參考了網上的一些代碼。這里大家要是看不 { //懂或是不想看,就略過吧。還有,這里“結點”寫成“節點”了,糾正一下,不好意思 struct Student *f, *t, *p=NULL, *q; f = h->next; //f指向舊鏈的第一個節點,即等待在新鏈中“安家落戶”(插入)的節點 h->next = NULL; //將原鏈的第一個節點單拿出來作為新鏈(待插入鏈)的第一個節點,默認此節點是關鍵值最大的節點 while (f!=NULL) //當f=NULL,舊鏈中的節點都插入到了新鏈,排序完成 { for (t = f, q = h; (q != NULL && (q->score > t->score)); p = q, q = q->next);//t和f同指,當找到插入位置,f指向舊鏈的下一個節點時,用t來進行 //插入操作;q先指向新鏈的第一個節點,q不斷在新鏈中后移,以找到f(即t)所指節點的插入位置 //p作為q的前驅,用來完成插入。整個語句的作用是:在新鏈遍歷完(q != NULL)的前提下,在新 //鏈中找到第一個關鍵值比f(即t)所指節點關鍵值小的節點,毫無疑問,q的前驅,即p(如果有的 //話)的關鍵值一大於定f(即t)所指節點關鍵值(否則q怎么會后移到當前位置呢?);如果沒有, //那說明當前新鏈的頭節點關鍵值比f(即t)所指節點關鍵值小;如果最后q = NULL了,說明當前新 //鏈的最后一個節點(此時p正指向它)的關鍵值都比f(即t)所指節點關鍵值大。不管哪種情況,f //(即t)所指節點都應插在q所指節點前,p所指節點后(如果有的話) f = f->next; //在進行插入操作前,先使f后移 if (q == h) h = t; //如果當前新鏈的頭節點關鍵值比f(即t)所指節點關鍵值小,需要將f(即t)所指節點插在該頭節點前,先讓新鏈頭節點指針指向 //f(即t)所指節點,作為新鏈的新的頭節點 else p->next = t; //否則,將f(即t)所指節點連在p所指節點后 t->next = q; //不管if還是else,都需要將f(即t)所指節點連在q所指節點前,如果q=NULL,就是讓f(即t)所指節點的next域指向NULL,這顯然也是正確的 } return h; //返回新鏈(排好序的鏈)的頭節點指針 } struct Student* sort2(struct Student* h) //選擇排序 { struct Student *f=NULL,*t=NULL, *max, *maxbf=NULL, *p; while (h!=NULL) { for (p = h, max = h; p->next != NULL; p = p->next) { if (p->next->score > max->score) { maxbf = p; max = p->next; } } if (f==NULL) { f = max; t = max; } else { t->next = max; t = max; } if (max==h) { h = h->next; } else { maxbf->next = max->next; } } t->next = NULL; return f; } struct Student* sort3(struct Student* h) //這是什么排序呢?我也說不好。這是我自己想出來的算 { //法……大體思想是:先從鏈表第一個結點開始遍歷鏈表,找出關鍵值(這里是成績score)最大的(因為 struct Student *p, *q, *pt=NULL, *pbf=NULL, *qbf=NULL; //是從大到小排序)結點和鏈表中第一個結點交換(利用指針實現);然后,從鏈表中第二個結點開始遍歷鏈 for (p = h ; p->next!=NULL; pbf = p, p = p->next) //表,找出關鍵值最大的結點和鏈表中第二個結點交換……如此操作,直到從鏈表中最后一個結點開始的那趟 { //遍歷和操作結束 for (q = p; q->next != NULL;q=q->next) //代碼格式很不好,寫這段代碼時在下還很渣很渣……粘貼到這里時,就更不好看了……對不起大家了 { if (p->score < q->next->score) { qbf = q; q = q->next; if (p==h && p->next==q) { h = q; p->next = q->next; q->next = p; p = q; } else { if (p == h&&p->next != q) { h = q; pt = q->next; q->next = p->next, qbf->next = p; p->next = pt; p = q; q = qbf; } else { if (p != h && p->next == q) { pt = q->next; pbf->next = q; q->next = p; p->next = pt; p = q; } else { if (p != h && p->next != q) { pt = q->next; pbf->next = q; q->next = p->next; qbf->next = p; p->next = pt; p = q; q = qbf; } } } } } } } return h; } //快排 這里在下也參考了網上的代碼,但在下也着實進行了一番改進才編譯通過,這里使用了指針的指針,不詳細講了,大家自己分析吧 struct Student* Link_Quick_Sort(struct Student ** head, struct Student ** end) // 注意這里函數返回值可以寫成void,同時將return語句去掉, { //同時,將main函數中(1)(2)兩句改為: struct Student * big_head=NULL, *big_end=NULL, *small_head=NULL, *small_end=NULL; //Link_Quick_Sort(&pt, NULL); struct Student * big_tail=NULL, *small_tail = NULL; //for (p=pt, i = 1; i <= n; i++, p = p->next) int key = (*head)->score; //也是可以的。原因是遞歸是先進后出,后進先出,二第一次調用時傳的是&pt(見main函數中 struct Student * traversal = (*head)->next; //第(1)句),故當整個函數結束后,pt的值已修改,且指向排好序的鏈表的頭結點。 (*head)->next = NULL; struct Student *p = NULL; while (traversal != NULL) { if (traversal->score > key) { if (big_head == NULL) { big_head = traversal; big_tail = traversal; } else{ big_tail->next = traversal; big_tail = traversal; } traversal = traversal->next; big_tail->next = NULL; } else { if (small_head == NULL) { small_head = traversal; small_tail = traversal; } else{ small_tail->next = traversal; small_tail = traversal; } traversal = traversal->next; small_tail->next = NULL; } } big_end = big_tail; small_end = small_tail; if (big_head != NULL && big_head->next != NULL){ Link_Quick_Sort(&big_head, &big_end); } if (small_head != NULL && small_head->next != NULL){ Link_Quick_Sort(&small_head, &small_end); } if (big_end != NULL&&small_head != NULL) { big_end->next = (*head); (*head)->next = small_head; (*head) = big_head; if (end == NULL){ end = &p; } (*end) = small_end; } else if (big_end!=NULL) { big_end->next = (*head); if (end == NULL){ end = &p; } (*end) = (*head); (*head) = big_head; } else if (small_head!=NULL) { (*head)->next = small_head; if (end == NULL){ end = &p; } (*end) = small_end; } return (*head); } void main() //用main函數來測試 { printf("請依次輸入學生的學和姓名\n"); printf("學號和姓名間以空格隔開\n"); printf("輸入0 0結束\n"); struct Student* pt,*p; struct Student* creat(); struct Student* sort(); //這里調用的是冒泡排序,要想調用其它排序,在這里改一下函數調用就可以了 pt=creat(); int i; for ( p=pt,i = 1; i <=n; i++,p=p->next) printf("num=%ld score=%d\n", p->num, p->score); printf("排序后:\n"); p=Link_Quick_Sort(&pt, NULL); //(1) for ( i = 1; i <= n; i++, p = p->next)//(2) printf("第%d名: num=%ld score=%d\n",i, p->num, p->score); } 代碼已經過測試,在VS2013上成功運行! 發此文有兩大目的: 1.和大家交流經驗,供需要的人參考。 2.在下菜鳥,代碼中難免有不妥之處,懇求大神批評指正。您的批評就是在下提高的起點,對於您的批評,在下將不勝感激!