文字描述
当每个记录所占空间较多,即每个记录存放的除关键字外的附加信息太大时,移动记录的时间耗费太大。此时,就可以像表插入排序、链式基数排序,以修改指针代替移动记录。但是有的排序方法,如快速排序和堆排序,无法实现表排序。这种情况下就可以进行“地址排序”,即另设一个地址向量指示相应记录的位置;同时在排序过程中不移动记录而移动记录地址向量中相应分量的内容。见示意图,(b)是排序结束后的地址向量,地址相连adr[1,…,8]中的值表示排序后的记录的次序,r[adr[1]]为最小记录,r[adr[8]]为最大记录。
如果需要的话,可根据adr的值重排记录的物理位置。重排算法如下:
比如要根据示意图中的(b)中地址向量重排原记录的话,由于(b)中的adr[1]=6, 则在暂存R(49)后,需要将R(13)从r[6]的位置移动到r[1]。又因为adr[6]=2,则将R(65)从r[2]的位置移至r[6]的位置。同理,将R(27)移至r[2]的位置,此时因adr[4]=1,则之前暂存的R(49)应该放在r[4]的位置上。至此完成一个调整记录位置的小循环,此小循环完成后的记录及地址向量的状态如示意图(c)所示。
示意图
算法分析
地址排序不能算是独立的算法,只是在之前讨论的内部排序算法中,另设一个地址向量,用移动地址向量中的分量值代替移动记录而已。
地址重排算法中,因为每个小循环要暂存一个记录,所以辅助空间为1
地址重排算法中,除需要暂存的记录外,所有记录均一次到位。而每个小循环至少移动两个记录,则这样的小循环最多n/2个,所以重排算法中至多移动记录[3n/2]次,其时间复杂度为n。
代码实现
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #define DEBUG 5 6 #define EQ(a, b) ((a) == (b)) 7 #define LT(a, b) ((a) < (b)) 8 #define LQ(a, b) ((a) <= (b)) 9 10 #define MAXSIZE 100 11 #define INF 1000000 12 typedef int KeyType; 13 typedef char InfoType; 14 typedef struct{ 15 KeyType key; 16 InfoType otherinfo; 17 }RedType; 18 19 typedef struct{ 20 RedType r[MAXSIZE+1]; 21 //地址向量 22 int adr[MAXSIZE+1]; 23 int length; 24 }SqList; 25 26 void PrintList(SqList L){ 27 int i = 0; 28 printf("下标值:"); 29 for(i=0; i<=L.length; i++){ 30 printf("[%d] ", i); 31 } 32 printf(" 关键字:"); 33 for(i=0; i<=L.length; i++){ 34 if(EQ(L.r[i].key, INF)){ 35 printf(" %-3c", '-'); 36 }else{ 37 printf(" %-3d", L.r[i].key); 38 } 39 } 40 printf(" 其他值:"); 41 for(i=0; i<=L.length; i++){ 42 printf(" %-3c", L.r[i].otherinfo); 43 } 44 printf(" 地址值:"); 45 for(i=0; i<=L.length; i++){ 46 printf(" %-3d", L.adr[i]); 47 } 48 printf(" "); 49 return ; 50 } 51 52 //堆采用顺序存储表示 53 typedef SqList HeapType; 54 55 /* 56 *已知H->r[s,...,m]中记录的关键字除H->r[s].key之外均满足的定义 57 *本函数调整H-r[s]的关键字,使H->r[s,...,m]成为一个大顶堆(对其中 58 *记录的关键字而言) 59 */ 60 void HeapAdjust(HeapType *H, int s, int m) 61 { 62 // RedType rc = H->r[s]; 63 RedType rc = H->r[H->adr[s]]; 64 int rc_adr = H->adr[s]; 65 int j = 0; 66 //沿key较大的孩子结点向下筛选 67 for(j=2*s; j<=m; j*=2){ 68 //j为key较大的纪录的下标 69 if(j<m && LT(H->r[H->adr[j]].key, H->r[H->adr[j+1]].key)) 70 j+=1; 71 //rc应该插入位置s上 72 if(!LT(rc.key, H->r[H->adr[j]].key)) 73 break; 74 H->adr[s] = H->adr[j]; 75 s = j; 76 } 77 //插入 78 H->adr[s] = rc_adr; 79 } 80 81 /* 82 * 对顺序表H进行堆排序 83 */ 84 void HeapSort(HeapType *H) 85 { 86 #ifdef DEBUG 87 printf("开始堆排序,和之前的堆排序不同之处在于,移动地址向量代替移动记录。 "); 88 #endif 89 int i = 0; 90 //把H->r[1,...,H->length]建成大顶堆 91 for(i=H->length/2; i>=1; i--){ 92 HeapAdjust(H, i, H->length); 93 } 94 #ifdef DEBUG 95 printf("由一个无序序列建成一个初始大顶堆: "); 96 PrintList(*H); 97 #endif 98 int tmp; 99 for(i=H->length; i>1; i--){ 100 //将堆顶记录和当前未经排序子序列H->r[1,...,i]中最后一个记录相互交换 101 tmp = H->adr[1]; 102 H->adr[1] = H->adr[i]; 103 H->adr[i] = tmp; 104 //将H->r[1,...,i-1]重新调整为大顶堆 105 HeapAdjust(H, 1, i-1); 106 #ifdef DEBUG 107 printf("调整1至%d的元素,使其成为大顶堆: ", i-1); 108 PrintList(*H); 109 #endif 110 } 111 } 112 113 /* 114 * adr给出顺序表H的有序次序,即L->r[adr[i]]是第i小的记录 115 * 本算法按adr重排H,使其有序 116 */ 117 void Rearrange(HeapType *H, int adr[]) 118 { 119 int i = 0, j =0, k = 0; 120 for(i=1; i<=H->length; i++){ 121 if(adr[i] != i){ 122 //暂存记录H->r[i] 123 H->r[0] = H->r[i]; 124 j = i; 125 //调整L->r[adr[j]]的记录到位直到adr[j] == i为止 126 while(adr[j] != i){ 127 k = adr[j]; 128 H->r[j] = H->r[k]; 129 adr[j] = j; 130 j = k; 131 } 132 //记录到序 133 H->r[j] = H->r[0]; 134 adr[j] = j; 135 #ifdef DEBUG 136 printf("第%d躺调整: ", i); 137 PrintList(*H); 138 #endif 139 } 140 } 141 } 142 143 int main(int argc, char *argv[]) 144 { 145 if(argc < 2){ 146 return -1; 147 } 148 HeapType H; 149 int i = 0; 150 for(i=1; i<argc; i++){ 151 if(i>MAXSIZE) 152 break; 153 H.r[i].key = atoi(argv[i]); 154 H.r[i].otherinfo = 'a'+i-1; 155 H.adr[i] = i; 156 } 157 H.length = (i-1); 158 H.r[0].key = H.adr[0] = 0; 159 H.r[0].otherinfo = '0'; 160 printf("输入数据: "); 161 PrintList(H); 162 //对顺序表H作堆排序 163 HeapSort(&H); 164 #ifdef DEBUG 165 printf("对排序后的顺序表按照地址向量重新调整记录位置,使其有序 "); 166 #endif 167 Rearrange(&H, H.adr); 168 PrintList(H); 169 return 0; 170 }
运行