鏈表的排序


鏈表的排序

2015/4/17 星期五 下午 18:25:04

一、順序表的排序

對順序表的排序其實就是對結構體中的關鍵字的排序。

c語言版:

  • 自定義結構體:

      typedef struct node
      {
          int age;
          int height;
          int width;
      }Node;
    

現在想根據其中的age排序,用c語言實現有兩種:

1、自定義交換函數,然后用常用的交換排序的方法進行排序就行了。這里用快速排序排序實現:

  • 快排實現:

      //自定義結構體交換
      void swap(Node *a, Node *b)  //指定排序的節點
      {
      		Node t = *a;
      		*a = *b;
      		*b = t;
      }
      //快排遞歸實現
      void QuickSort(ElemType *elem, int left, int right) //注意這里是閉區間
      {
          if(left < right)
          {
              int temp = elem[right].age;  //這里要指定是按age排序的
              int i = left-1;
              int j = left;
              while(j < right)
              {
                 if(elem[j].age <= temp)   //這里也要指定 <= 可以用<帶換
                 {
                    ++i;
                    swap(&elem[i], &elem[j]);
                 }
                 j++;
              }
              swap(&elem[i+1], &elem[right]);
              int r = i+1;
              QuickSort(elem, left, r-1);
              QuickSort(elem, r+1, right);
          }
      }
    
  • 算法平均復雜度:O(nlogn),最壞情況復雜度:O(n2)。空間復雜度O(n)。

2、 當然c語言中也內置了排序算法qsort,位於stdlib.h頭文件中,核心是用快排實現的。

  • 函數原型:

    void qsort(void *base, int nelem, int width, int (*fcmp)(const void *,const void *));   

    參數含義:1 待排序數組首地址。 2 數組中待排序元素數量。 3 各元素的占用空間大小。 4 指向函數的指針

  • 函數關鍵的比較函數:
    這個函數是自定義對對象如何排序的方法。可以對整數,對字符串,對自定義結構體,都可以排序,從大到小,或者從小到大。比較函數一般這樣寫:

      int cmp(const void* a, const void* b)
      {
         return  (*(Node*)a).age - (*(Node*)b).age;
      }    
    

    結構體節點比較就是這樣寫。返回的是一個整型值,

    也可以把減號換為>號,就是從大到小排序的。

    比較函數返回大於0,qsort就認為 a>b , 如果比較函數返回等於0

    qsort就認為a 和b 這兩個元素相等,返回小於零 qsort就認為 a<b.

  • 主函數中調用:

    qsort(a,10,sizeof(a[0]),cmp);

c++版:

由於c++是包括了c語言,所以上面的快排,在c++中依然能運行通過。
這里,我來介紹一下C++中更好地辦法去排序。

1、使用重載運算符,達到給結構體新的比較方法,來實現排序的目的。說白了就是創建結構體,定義兩個結構體比較的時候,是按哪個字段比較。直接上代碼:
注意:c++中可以不帶typedef 來給結構體"披外套.

struct Node
{
	int age;
	itn height;
	int width;
	bool operator<(struct Node b) const  //這里必須加上const
	{
		return age < b.age;
	}
}; 

經過上面的定義后,接着寫

Node a, b; 並給字段賦過值后,再比較a<b. 含義就是a.age < b.age了。

這樣的話,上面的快速排序的代碼,就不用特意指定按age排序了。結構體可以直接賦值,這里就不用重載=號了。
交換代碼和上面的一樣。改寫上面的快速排序代碼:

void QuickSort(ElemType *a, int left, int right)
{
	if(left < right)
	{
		ElemType temp = a[right];  //這里不用指定是age,具有通用型。
		int i = left-1;
		int j = left;
		while(j < right)
		{
			if(a[j] < a[right])
			{
				++i;
				Swap(&a[j],&a[i]);
			}
			j++;
		}
		Swap(&a[i+1],&a[right]);
		int r = i+1;
		QuickSort(a, left, r-1);
		QuickSort(a, r+1, right);
	}
}

2、使用sort,同樣c++中也有特有的排序函數,sort,它的內部是很多高效率的排序算法的整合,根據待排序的數據量選擇不同的排序方法,是時間最優。
該函數位於頭文件#include <algorithm.h>中。

  • 函數原型:使用了模板類, 就是第三個參數(自定義排序方式)是可選的。

template< class RandomIt >

void sort( RandomIt first, RandomIt last ) (1);

template< class RandomIt, class Compare > 

void sort( RandomIt first, RandomIt last, Compare comp ); (2)

  • sort使用的c++中的模板template, 就可以對任何定義了比較關系的集合進行排序了。模板類,相當於純面向對象語言,如c#和Java,中的泛型概念。

  • 如何使用sort?

    1、配合結構體中的重載一起使用:
    我們在上面定義了重載’<‘運算符的結構體,就可以直接用sort排序了。
    直接sort(node,node+n);兩個參數,相當於數組的始末指針。
    不過注意,這里是按從小到大的順序排的,因為上面重載的時候是<對<號,如果重載小於號的時候,在函數體內寫的卻是age>b.age,結果就是從大到小排序了。

    2、不使用結構體運算符的重載,使用sort的第三個參數(自定義排序方法)來實現排序的效果。

       bool cmp(ElemType a, ElemType b)
       {
          return a.age > b.age;
       }
    

    這樣的話主函數里用的時候寫sort(a,a+n,cmp);就能排序了。

二、鏈式表的排序

  單鏈表不適合快速排序,雙向鏈表才適合快速排序,這里我們用選擇排序法排序單鏈表,用快速排序法,排序雙向鏈表:

  • 單鏈表
    定義單鏈表:

       typedef struct LinkNode
       {
          DataType data;
          struct LinkNode *next;  //指向后繼節點
       }Linknode;
    

有關選擇排序的知識,請看這里:wikipedia:選擇排序
直接上代碼:

     void  Sort(LinkNode *L)
     {
     		LinkNode *p = L->next;
     		LinkNode *min = p;
     		
     		while(p)
     		{
     		   min = p;
     		   LinkNode *q = L->next;
     		   while(q)
     		   {
     		      if(q->data < min->data)
     		      {
     		         min = q;
     		      }
     		      q = q->next;
     		   }
     		   Swap(p, min);  //這里很關鍵,如何交換兩個節點的值,而不改變節點的指針指向?
     		   p = p->next;
     		}
     }

下面畫圖來解釋:

實際代碼:

inline void Swap(LinkNode *p, Linknode *q)
{
     LinkNode temp = *p;
     temp.next = q->next;
     q->next = p->next;
     *p = *q;
     *q = temp; 
}
  • 雙向鏈表實現快速排序

    1、雙向鏈表定義:

      struct Node
      {
          int data;
          struct Node *pri, *next;
      };
    

    2、新建雙鏈表

      Node* CreateDulNode(int *a, int n) //將數組元素存到鏈表中
      {
          Node *head = (Node*)malloc(sizeof(Node));
          head->next = NULL;
          head->pri = NULL;
    
          Node *p = head;
          int i = 0;
          Node *q = NULL;
          while (i<n)
          {
              q = (Node*)malloc(sizeof(Node));
              q->data = a[i];
              q->next = p->next;
              q->pri = p;
              p->next = q;
              p = q;
              i++;
          }
          q->next = head;
          head->pri = q;
          return head;
      }
    

    3、快速排序 //其實就是數組的排序原理,有些小細節要注意

       //調用QuickSort(L->next, L->pri);
       void  QuickSort(Node *Left, Node* right) //閉區間
       {
      	if (Left->pri != right) //這里當right在left左邊是結束
      	{
      		Node *temp = right;
      		Node *p = Left->pri;
      		Node *q = Left;
      		while (q != right)
      		{
      			if (q->data < temp->data)
      			{
      				p = p->next;
      				Swap(p, q);
      			}
      			q = q->next;
      		}
      		p = p->next;
      		Swap(p, temp);
      	//	OutPut2(Left,right); //測試輸出函數
      		QuickSort(Left, p->pri); 
      		QuickSort(p->next, right);
      	}
      }
    

    4、關鍵的交換函數,不過也和單鏈表的交換差不多了

      void Swap(Node *p, Node *q)
      {
      	Node temp = *p;
      	temp.next = q->next;
      	temp.pri = q->pri;
      	q->next = p->next;
      	q->pri = p->pri;
      	*p = *q;
      	*q = temp;
      }
    

    5、測試輸出函數

      void OutPut2(Node *Left, Node *right)
      {
      	Node *p = Left;
      	while (p!=right)
      	{
      		cout << p->data << " ";
      		p = p->next;
      	}
      	cout << right->data << " ";  //單獨輸出最后一個
      	cout << endl;
      }
    

    實驗結果:

    總結: 以前排序都是在數組上排序,現在學了鏈表,也想實現快速排序。總之,本質上沒啥區別,算法流程都差不多,只是操作上的不同。


免責聲明!

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



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