C語言實現單向鏈表及其各種排序(含快排,選擇,插入,冒泡)


 #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.在下菜鳥,代碼中難免有不妥之處,懇求大神批評指正。您的批評就是在下提高的起點,對於您的批評,在下將不勝感激! 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM