文字描述

  當每個記錄所佔空間較多,即每個記錄存放的除關鍵字外的附加資訊太大時,移動記錄的時間耗費太大。此時,就可以像表插入排序、鏈式基數排序,以修改指標代替移動記錄。但是有的排序方法,如快速排序和堆排序,無法實現表排序。這種情況下就可以進行“地址排序”,即另設一個地址向量指示相應記錄的位置;同時在排序過程中不移動記錄而移動記錄地址向量中相應分量的內容。見示意圖,(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。

程式碼實現

 #include <stdio.h>
#include <stdlib.h> #define DEBUG #define EQ(a, b) ((a) == (b))
#define LT(a, b) ((a) < (b))
#define LQ(a, b) ((a) <= (b)) #define MAXSIZE 100
#define INF 1000000
typedef int KeyType;
typedef char InfoType;
typedef struct{
KeyType key;
InfoType otherinfo;
}RedType; typedef struct{
RedType r[MAXSIZE+];
//地址向量
int adr[MAXSIZE+];
int length;
}SqList; void PrintList(SqList L){
int i = ;
printf("下標值:");
for(i=; i<=L.length; i++){
printf("[%d] ", i);
}
printf("\n關鍵字:");
for(i=; i<=L.length; i++){
if(EQ(L.r[i].key, INF)){
printf(" %-3c", '-');
}else{
printf(" %-3d", L.r[i].key);
}
}
printf("\n其他值:");
for(i=; i<=L.length; i++){
printf(" %-3c", L.r[i].otherinfo);
}
printf("\n地址值:");
for(i=; i<=L.length; i++){
printf(" %-3d", L.adr[i]);
}
printf("\n\n");
return ;
} //堆採用順序儲存表示
typedef SqList HeapType; /*
*已知H->r[s,...,m]中記錄的關鍵字除H->r[s].key之外均滿足的定義
*本函式調整H-r[s]的關鍵字,使H->r[s,...,m]成為一個大頂堆(對其中
*記錄的關鍵字而言)
*/
void HeapAdjust(HeapType *H, int s, int m)
{
// RedType rc = H->r[s];
RedType rc = H->r[H->adr[s]];
int rc_adr = H->adr[s];
int j = ;
//沿key較大的孩子結點向下篩選
for(j=*s; j<=m; j*=){
//j為key較大的紀錄的下標
if(j<m && LT(H->r[H->adr[j]].key, H->r[H->adr[j+]].key))
j+=;
//rc應該插入位置s上
if(!LT(rc.key, H->r[H->adr[j]].key))
break;
H->adr[s] = H->adr[j];
s = j;
}
//插入
H->adr[s] = rc_adr;
} /*
* 對順序表H進行堆排序
*/
void HeapSort(HeapType *H)
{
#ifdef DEBUG
printf("開始堆排序,和之前的堆排序不同之處在於,移動地址向量代替移動記錄。\n");
#endif
int i = ;
//把H->r[1,...,H->length]建成大頂堆
for(i=H->length/; i>=; i--){
HeapAdjust(H, i, H->length);
}
#ifdef DEBUG
printf("由一個無序序列建成一個初始大頂堆:\n");
PrintList(*H);
#endif
int tmp;
for(i=H->length; i>; i--){
//將堆頂記錄和當前未經排序子序列H->r[1,...,i]中最後一個記錄相互交換
tmp = H->adr[];
H->adr[] = H->adr[i];
H->adr[i] = tmp;
//將H->r[1,...,i-1]重新調整為大頂堆
HeapAdjust(H, , i-);
#ifdef DEBUG
printf("調整1至%d的元素,使其成為大頂堆:\n", i-);
PrintList(*H);
#endif
}
} /*
* adr給出順序表H的有序次序,即L->r[adr[i]]是第i小的記錄
* 本演算法按adr重排H,使其有序
*/
void Rearrange(HeapType *H, int adr[])
{
int i = , j =, k = ;
for(i=; i<=H->length; i++){
if(adr[i] != i){
//暫存記錄H->r[i]
H->r[] = H->r[i];
j = i;
//調整L->r[adr[j]]的記錄到位直到adr[j] == i為止
while(adr[j] != i){
k = adr[j];
H->r[j] = H->r[k];
adr[j] = j;
j = k;
}
//記錄到序
H->r[j] = H->r[];
adr[j] = j;
#ifdef DEBUG
printf("第%d躺調整:\n", i);
PrintList(*H);
#endif
}
}
} int main(int argc, char *argv[])
{
if(argc < ){
return -;
}
HeapType H;
int i = ;
for(i=; i<argc; i++){
if(i>MAXSIZE)
break;
H.r[i].key = atoi(argv[i]);
H.r[i].otherinfo = 'a'+i-;
H.adr[i] = i;
}
H.length = (i-);
H.r[].key = H.adr[] = ;
H.r[].otherinfo = '';
printf("輸入資料:\n");
PrintList(H);
//對順序表H作堆排序
HeapSort(&H);
#ifdef DEBUG
printf("對排序後的順序表按照地址向量重新調整記錄位置,使其有序\n");
#endif
Rearrange(&H, H.adr);
PrintList(H);
return ;
}

堆排序(採用地址排序)_地址重排演算法

執行