數據結構學習筆記06排序 (快速排序、表排序、基數排序)


1.快速排序

不穩定

分而治之

 

找主元pivot,小於主元划分為一個子集,大於主元的划分為一個子集

然后進行遞歸

最好情況:每次主元正好中分,T(N) = O( NlogN )

 

選主元 的方法有很多,這里用 取頭、中、尾的中位數。

  直接選A[0]為pivot,時間復雜度T ( N ) = O( N ) + T ( N–1 ) = O( N ) + O ( N–1 ) + T( N–2 ) = = O( N ) + O ( N–1 ) + …+ O( 1 ) = O( N^2 )

  隨機取pivot:rand()函數 也很費

  取頭、中、尾的中位數……

子集划分

i指向8,j指向7.

8>6,i不動,7>6,j--;j指向2,2<6,j不動 交換8和2;

i++,i指向1,1<6,i++;i指向4,4<6,i++;i指向9,9>6,i不動;

j--,j指向5,5<6,j不動,交換5和9;

i++,i指向0,0<6,i++;i指向3,3<6,i++;i指向9,9>6,i不動;

j--,j指向3,3<6,j不動.

i>j,結束,6放到i位置,即6和9交換

如果有元素正好等於pivot怎么辦:
  停下來交換:會產生很多不必要的交換。(一串相等的序列)
  不理它,繼續移動指針:pivot位置靠近一段,最糟糕會在最右邊端,和pivot取A[0]一樣,O( N^2 )

  綜合考慮,選第一種,停下來交換。

小規模數據的處理
  快速排序的問題
    用遞歸……
    對小規模的數據(例如N不到100)可能還不如插入排序快
  解決方案
    當遞歸的數據規模充分小,則停止遞歸,直接調用簡單排序(例如插入排序)
    在程序中定義一個Cutoff的閾值

 1 #include <stdio.h>
 2 typedef int ElementType;
 3 
 4 void Swap( ElementType *a, ElementType *b )
 5 {
 6     ElementType t = *a;
 7     *a = *b; 
 8     *b = t;
 9 }
10 
11 void InsertionSort(ElementType A[], int N)
12 {     
13     int i;
14     for (int P = 1; P < N; P++ ) {
15         ElementType temp = A[P];     //取出未排序序列中第一個元素
16         for (i = P; i > 0 && A[i-1] > temp; i-- )
17             A[i] = A[i-1];     //依次與已排序序列中元素比較並右移
18         A[i] = temp; 
19     }
20 }
21 
22 /* 快速排序 */
23 ElementType Median3( ElementType A[], int Left, int Right )
24 { 
25     int Center = (Left+Right) / 2;
26     if ( A[Left] > A[Center] )
27         Swap( &A[Left], &A[Center] );
28     if ( A[Left] > A[Right] )
29         Swap( &A[Left], &A[Right] );
30     if ( A[Center] > A[Right] )
31         Swap( &A[Center], &A[Right] );
32     /* 此時A[Left] <= A[Center] <= A[Right] */
33     Swap( &A[Center], &A[Right-1] ); /* 將基准Pivot藏到右邊*/
34     /* 只需要考慮A[Left+1] … A[Right-2] */
35     return  A[Right-1];  /* 返回基准Pivot */
36 }
37  
38 void Qsort( ElementType A[], int Left, int Right )
39 { /* 核心遞歸函數 */ 
40      int Pivot, Cutoff = 50, Low, High;//閾值的定義
41        
42      if ( Cutoff <= Right-Left ) { /* 如果序列元素充分多,進入快排 */
43           Pivot = Median3( A, Left, Right ); /* 選基准 */ 
44           Low = Left; High = Right-1;
45           while (1) { /*將序列中比基准小的移到基准左邊,大的移到右邊*/
46                while ( A[++Low] < Pivot ) ;
47                while ( A[--High] > Pivot ) ;
48                if ( Low < High ) Swap( &A[Low], &A[High] );
49                else break;
50           }
51           Swap( &A[Low], &A[Right-1] );   /* 將基准換到正確的位置 */ 
52           Qsort( A, Left, Low-1 );    /* 遞歸解決左邊 */ 
53           Qsort( A, Low+1, Right );   /* 遞歸解決右邊 */  
54      }
55      else InsertionSort( A+Left, Right-Left+1 ); /* 元素太少,用簡單排序 */ 
56 }
57  
58 void QuickSort( ElementType A[], int N )
59 { /* 統一接口 */
60      Qsort( A, 0, N-1 );
61 }
62 
63 int main()
64 {
65     int a[] = {34,8,64,51,32,21};
66     QuickSort(a, 6);
67     for(int i = 0; i < 6; i++)
68         printf("%d ",a[i]);
69     return 0;
70 }
71 
72 QuickSort
QuickSort

 

 2.表排序

間接排序

 

 

 

 

定義一個指針數組(下標)作為“表”(table) 

如果僅要求按順序輸出,則輸出:A[ table[0] ], A[ table[1] ], ……, A[ table[N-1] ]

 

物理排序

 

 

 

 

 

N個數字的排列由若干個獨立的環組成

用temp記錄初值 ,每次換位子修改table值,用if ( table[i] == i )判斷一個環的結束

 

復雜度

  最好情況:初始即有序
  最壞情況:
  有[N / 2]向下取整 個環,每個環包含2個元素
  需要[N / 2]向下取整 次元素移動
  T = O( m N ) ,m 是每個A元素的復制時間。

 

3.基數排序

桶排序:假設我們有N 個學生,他們的成績是0到100之間的整數(於是有M = 101 個不同的成績值)。如何在線性時間內將學生按成績排序?

   T(N, M) = O( M+N )

  當M>>N時,桶排序不合算

次位優先LSB

假設我們有N = 10 個整數,每個整數的值在0到999之間(於是有M = 1000 個不同的值)。

輸入序列: 64, 8, 216, 512, 27, 729, 0, 1, 343, 125
用“次位優先”(Least Significant Digit)  T=O(P(N+B))

多關鍵字的排序

一副撲克牌是按2種關鍵字排序的

 

 1 #include <stdio.h> 
 2 #include <stdlib.h>
 3 
 4 typedef int ElementType;
 5 
 6 /* 基數排序 - 次位優先 */
 7  
 8 /* 假設元素最多有MaxDigit個關鍵字,基數全是同樣的Radix */
 9 #define MaxDigit 4
10 #define Radix 10
11  
12 /* 桶元素結點 */
13 typedef struct Node *PtrToNode;
14 struct Node {
15     int key;
16     PtrToNode next;
17 };
18  
19 /* 桶頭結點 */
20 struct HeadNode {
21     PtrToNode head, tail;
22 };
23 typedef struct HeadNode Bucket[Radix];
24   
25 int GetDigit ( int X, int D )
26 { /* 默認次位D=1, 主位D<=MaxDigit */
27     int d, i;
28      
29     for (i=1; i<=D; i++) {
30         d = X % Radix;
31         X /= Radix;
32     }
33     return d;
34 }
35  
36 void LSDRadixSort( ElementType A[], int N )
37 { /* 基數排序 - 次位優先 */
38      int D, Di, i;
39      Bucket B;
40      PtrToNode tmp, p, List = NULL; 
41       
42      for (i=0; i<Radix; i++) /* 初始化每個桶為空鏈表 */
43          B[i].head = B[i].tail = NULL;
44      for (i=0; i<N; i++) { /* 將原始序列逆序存入初始鏈表List */
45          tmp = (PtrToNode)malloc(sizeof(struct Node));
46          tmp->key = A[i];
47          tmp->next = List;
48          List = tmp;
49      }
50      /* 下面開始排序 */ 
51      for (D=1; D<=MaxDigit; D++) { /* 對數據的每一位循環處理 */
52          /* 下面是分配的過程 */
53          p = List;
54          while (p) {
55              Di = GetDigit(p->key, D); /* 獲得當前元素的當前位數字 */
56              /* 從List中摘除 */
57              tmp = p; p = p->next;
58              /* 插入B[Di]號桶尾 */
59              tmp->next = NULL;
60              if (B[Di].head == NULL)
61                  B[Di].head = B[Di].tail = tmp;
62              else {
63                  B[Di].tail->next = tmp;
64                  B[Di].tail = tmp;
65              }
66          }
67          /* 下面是收集的過程 */
68          List = NULL; 
69          for (Di=Radix-1; Di>=0; Di--) { /* 將每個桶的元素順序收集入List */
70              if (B[Di].head) { /* 如果桶不為空 */
71                  /* 整桶插入List表頭 */
72                  B[Di].tail->next = List;
73                  List = B[Di].head;
74                  B[Di].head = B[Di].tail = NULL; /* 清空桶 */
75              }
76          }
77      }
78      /* 將List倒入A[]並釋放空間 */
79      for (i=0; i<N; i++) {
80         tmp = List;
81         List = List->next;
82         A[i] = tmp->key;
83         free(tmp);
84      } 
85 }
86 
87 int main()
88 {
89     int a[] = {34,8,64,51,32,21};
90     LSDRadixSort(a, 6);
91     for(int i = 0; i < 6; i++)
92         printf("%d ",a[i]);
93     return 0;
94 }
LSD
 1 #include <stdio.h> 
 2 #include <stdlib.h>
 3 
 4 typedef int ElementType;
 5 
 6 /* 基數排序 - 主位優先 */
 7  
 8 /* 假設元素最多有MaxDigit個關鍵字,基數全是同樣的Radix */
 9  
10 #define MaxDigit 4
11 #define Radix 10
12  
13 /* 桶元素結點 */
14 typedef struct Node *PtrToNode;
15 struct Node{
16     int key;
17     PtrToNode next;
18 };
19  
20 /* 桶頭結點 */
21 struct HeadNode {
22     PtrToNode head, tail;
23 };
24 typedef struct HeadNode Bucket[Radix];
25   
26 int GetDigit ( int X, int D )
27 { /* 默認次位D=1, 主位D<=MaxDigit */
28     int d, i;
29      
30     for (i=1; i<=D; i++) {
31         d = X%Radix;
32         X /= Radix;
33     }
34     return d;
35 }
36  
37 void MSD( ElementType A[], int L, int R, int D )
38 { /* 核心遞歸函數: 對A[L]...A[R]的第D位數進行排序 */
39      int Di, i, j;
40      Bucket B;
41      PtrToNode tmp, p, List = NULL; 
42      if (D==0) return; /* 遞歸終止條件 */
43       
44      for (i=0; i<Radix; i++) /* 初始化每個桶為空鏈表 */
45          B[i].head = B[i].tail = NULL;
46      for (i=L; i<=R; i++) { /* 將原始序列逆序存入初始鏈表List */
47          tmp = (PtrToNode)malloc(sizeof(struct Node));
48          tmp->key = A[i];
49          tmp->next = List;
50          List = tmp;
51      }
52      /* 下面是分配的過程 */
53      p = List;
54      while (p) {
55          Di = GetDigit(p->key, D); /* 獲得當前元素的當前位數字 */
56          /* 從List中摘除 */
57          tmp = p; p = p->next;
58          /* 插入B[Di]號桶 */
59          if (B[Di].head == NULL) B[Di].tail = tmp;
60          tmp->next = B[Di].head;
61          B[Di].head = tmp;
62      }
63      /* 下面是收集的過程 */
64      i = j = L; /* i, j記錄當前要處理的A[]的左右端下標 */
65      for (Di=0; Di<Radix; Di++) { /* 對於每個桶 */
66          if (B[Di].head) { /* 將非空的桶整桶倒入A[], 遞歸排序 */
67              p = B[Di].head;
68              while (p) {
69                  tmp = p;
70                  p = p->next;
71                  A[j++] = tmp->key;
72                  free(tmp);
73              }
74              /* 遞歸對該桶數據排序, 位數減1 */
75              MSD(A, i, j-1, D-1);
76              i = j; /* 為下一個桶對應的A[]左端 */
77          } 
78      } 
79 }
80  
81 void MSDRadixSort( ElementType A[], int N )
82 { /* 統一接口 */
83     MSD(A, 0, N-1, MaxDigit); 
84 }
85 
86 int main()
87 {
88     int a[] = {34,8,64,51,32,21};
89     MSDRadixSort(a, 6);
90     for(int i = 0; i < 6; i++)
91         printf("%d ",a[i]);
92     return 0;
93 }
MSD

 

 


免責聲明!

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



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