標籤:下標 最小值 輸入 amp 樹的定義 red while 排序演算法
文字描述
樹形選擇排序又稱錦標賽排序; 比如,在8個運動員中決出前3名至多需要11場比賽, 而不是7+6+5=18場比賽(它的前提是甲勝乙,乙勝丙,則甲必能勝丙)
首先對n個記錄的關鍵字進行兩兩比較,然後在(n/2)個較小者之間再進行兩兩比較,直至選出最小關鍵字的記錄為止,這個過程可用一顆有n個葉子結點的完全二叉樹表示。關於完全二叉樹的定義和與本排序演算法用到的性質見附錄1
演算法分析
由於含n個葉子結點的完全二叉樹的深度為[log2n]+1, 則在樹形選擇排序中,除了最小關鍵字外,每選擇一個次小關鍵字僅需進行log2n次比較,因此它的時間複雜度為nlogn.。但是它需要的輔助空間為2*n-1。而且它在選擇過程中,和"最大值"進行了多餘的比較。 另外,該演算法是不穩定的。
代碼實現
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <math.h> 4 /* 5 * double log2(double x); 以2為底的對數 6 * double ceil(double x); 取上整 7 * double floor(double x); 取下整 8 * double fabs(double x); 取絕對值 9 */ 10 11 #define DEBUG 12 13 #define EQ(a, b) ((a) == (b)) 14 #define LT(a, b) ((a) < (b)) 15 #define LQ(a, b) ((a) <= (b)) 16 17 #define MAXSIZE 100 18 #define INF 1000000 19 typedef int KeyType; 20 typedef char InfoType; 21 typedef struct{ 22 KeyType key; 23 InfoType otherinfo; 24 }RedType; 25 26 typedef struct{ 27 RedType r[MAXSIZE+1]; 28 int length; 29 }SqList; 30 31 void PrintList(SqList L){ 32 int i = 0; 33 printf("下標值:"); 34 for(i=0; i<=L.length; i++){ 35 printf("[%d] ", i); 36 } 37 printf("\n關鍵字:"); 38 for(i=0; i<=L.length; i++){ 39 if(EQ(L.r[i].key, INF)){ 40 printf(" %-3c", ‘-‘); 41 }else{ 42 printf(" %-3d", L.r[i].key); 43 } 44 } 45 printf("\n其他值:"); 46 for(i=0; i<=L.length; i++){ 47 printf(" %-3c", L.r[i].otherinfo); 48 } 49 printf("\n\n"); 50 return ; 51 } 52 53 /*樹形選擇排序演算法*/ 54 void TreeSelectSort(SqList *L) 55 { 56 //為實現該排序所需的輔助樹 57 SqList tree; 58 //輔助樹的大小 59 tree.length = L->length-1 + L->length; 60 61 tree.r[0].otherinfo = ‘0‘; 62 int i = 0, low = 1; 63 //由後向前填充此樹的葉子結點 64 for(i=0; i<L->length; i++){ 65 tree.r[tree.length-i] = L->r[L->length-i]; 66 } 67 //由後向前填充此樹的非葉子結點 68 for(i=(tree.length-L->length); i>=1; i--){ 69 tree.r[i] = (LT(tree.r[2*i].key, tree.r[2*i+1].key)?tree.r[2*i]:tree.r[2*i+1]); 70 } 71 //紀錄當前輔助樹的最小結點 72 RedType minred; 73 //記錄最小結點在葉子結點中的下標值 74 int minindex = 0; 75 while(low <= L->length){ 76 minred = tree.r[1]; 77 #ifdef DEBUG 78 printf("第%d趟樹形選擇排序後,輸出當前數最小值%d, %c\n", low, minred.key, minred.otherinfo); 79 PrintList(tree); 80 #endif 81 //不斷移走最小結點 82 L->r[low++] = minred; 83 minindex = tree.length; 84 //找到最小值在輔助樹葉子結點中的下標值 85 for(minindex=tree.length; (minindex>(tree.length-L->length)); minindex--){ 86 if(EQ(tree.r[minindex].key, minred.key) && EQ(tree.r[minindex].otherinfo, minred.otherinfo)){ 87 break; 88 } 89 } 90 //設定一個最大值標誌,INF表示無窮大 91 tree.r[minindex].key = INF; 92 //重新調整此輔助樹,使根結點關鍵字值最小 93 for(i=(minindex/2); i>=1; i/=2){ 94 tree.r[i] = (LT(tree.r[2*i].key, tree.r[2*i+1].key)?tree.r[2*i]:tree.r[2*i+1]); 95 } 96 } 97 #ifdef DEBUG 98 printf("按照樹形選擇排序後的原順序表:\n"); 99 PrintList(*L);100 #endif101 }102 103 int main(int argc, char *argv[])104 {105 if(argc < 2){106 return -1;107 }108 SqList L;109 int i = 0;110 for(i=1; i<argc; i++){111 if(i>MAXSIZE)112 break;113 L.r[i].key = atoi(argv[i]);114 L.r[i].otherinfo = ‘a‘+i-1;115 }116 L.length = (i-1);117 L.r[0].key = 0;118 L.r[0].otherinfo = ‘0‘;119 printf("輸入資料:\n");120 PrintList(L);121 //對順序表L作樹形選擇排序122 TreeSelectSort(&L);123 return 0;124 }樹形選擇排序
運行
附錄1 完全二叉樹
定義:設二叉樹深度為h,除第h層外,其他各層(1 ~ h-1)的結點數都達到最大個數,第h層所有的結點都集中在最左邊,這就是完全二叉樹。
性質:
1] 結點i (i>1)的雙親結點為i/2
2] 結點i的左孩子結點為2*i, 右孩子結點為2*i+1
3] 葉子結點數n0, 度為2(有左、右孩子結點)的結點數n2, 則n0 = n2+1
性質3]證明:
設n1為二叉樹中度為1的結點數。因為二叉樹中所有結點數的度均不大於2。所以二叉樹的結點數n = n0 + n1 +n2 -- (1)。
又除根結點外,其餘結點都有一個分支進入,設B為分支總數,則n=B+1。由於這些分支是由度為1或2的結點射出的,所以又有B=n1+2*n2,於是得n = B+1 = n1+2*n2+1 – (2)。
由(1)和(2)知, n0 = n2 +1
內部排序->選擇排序->樹形選擇排序