1. 程式人生 > >次優查詢樹的實現【嚴蔚敏】

次優查詢樹的實現【嚴蔚敏】

最優查詢樹: 用數學公式來表示就是:使得PH = \sum_{i=1}^{n}{\omega_{i}h_{i}}的PH值最小的樹為該陣列的靜態最優查詢樹。其中i為節點標號,\omega _{i}為節點i的帶權路徑長度,\omega _{i} = c _{i} * p _{i},它等於結點i的查詢路徑長度c,乘以該結點被查詢的概率p;h表示節點i在搜尋樹中的高度。通俗點來說,就是權值越大的結點,越放到靠近根結點的位置。可能查詢概率大,需要的比較次數(折半查詢中的次數)多,或者兩者皆有。 次優二叉樹: 選出一個結點,使得它左右兩側的子陣列的權值累加和之差的絕對值最小。把這個結點當做根節點,遞迴地用剛才的左右字陣列構造它的左右子樹。
數學表示式:\Delta P_{i} = |\sum_{j=i+1}^{h}{\omega _{j}} - \sum_{j=l}^{i-1}{\omega _{j}}|
Status SecondOptimal(BiTree &T, ElemType R[],int sw[],int low,int high)
 { // 由有序表R[low..high]及其累計權值表sw(其中sw[0]==0)遞迴構造
   // 次優查詢樹T。演算法9.3
   int i,j;
   double min,dw;
   i=low;
   min=fabs(sw[high]-sw[low]);
   dw=sw[high]+sw[low-1];
   for(j=low+1;j<=high;++j) // 選擇最小的△Pi值
      if(fabs(dw-sw[j]-sw[j-1])<min)
      {
       i=j;
       min=fabs(dw-sw[j]-sw[j-1]);
     }
   if(!(T=(BiTree)malloc(sizeof(BiTNode))))
     return ERROR;
     
        T->data=R[i]; // 生成結點
   if(i==low)
        T->lchild=NULL; // 左子樹空
   else
        SecondOptimal(T->lchild,R,sw,low,i-1); // 構造左子樹
   if(i==high)
        T->rchild=NULL; // 右子樹空
   else
        SecondOptimal(T->rchild,R,sw,i+1,high); // 構造右子樹
   return OK;
 }


void FindSW(int sw[],SSTable ST)
 { // 按照有序表ST中各資料元素的Weight域求累計權值表sw
   int i;
   sw[0]=0;
   for(i=1;i<=ST.length;i++)
     sw[i]=sw[i-1]+ST.elem[i].weight;
 }


Status CreateSOSTree(SOSTree &T,SSTable ST)
 { // 由有序表ST構造一棵次優查詢樹T。ST的資料元素含有權域weight。演算法9.4
   if(ST.length==0)
     T=NULL;
   else
   {
     FindSW(sw,ST); // 按照有序表ST中各資料元素的Weight域求累計權值表sw
     SecondOptimal(T,ST.elem,sw,1,ST.length);
   }
   return OK;
 }
程式碼完整實現:

 // algo9-3.cpp 靜態查詢表(靜態樹表)的操作
 #include <stdio.h>
 #include <malloc.h>
 #include <math.h>
 #include<iostream>             //cout,cin
using namespace std; 
 // 函式結果狀態程式碼
 #define TRUE 1
 #define FALSE 0
 #define OK 1
 #define ERROR 0
 #define INFEASIBLE -1
 // #define OVERFLOW -2 因為在math.h中已定義OVERFLOW的值為3,故去掉此行
 typedef int Status; // Status是函式的型別,其值是函式結果狀態程式碼,如OK等
 typedef int Boolean; // Boolean是布林型別,其值是TRUE或FALSE
 #define N 9 // 資料元素個數
 #define EQ(a,b) ((a)==(b))
 #define LT(a,b) ((a)<(b))
 #define LQ(a,b) ((a)<=(b))
 typedef char KeyType; // 設關鍵字域為字元型
 struct ElemType // 資料元素型別(以教科書例9-1為例)
 {
   KeyType key;
   int weight;
 }r[N]={{'A',1},{'B',1},{'C',2},{'D',5},{'E',3},
       {'F',4},{'G',4},{'H',3},{'I',5}}; // 全域性變數
 int sw[N+1]; // 累計權值,全域性變數
 typedef ElemType TElemType;
  typedef struct
 {
   ElemType *elem; // 資料元素儲存空間基址,建表時按實際長度分配,0號單元留空
   int length; // 表長度
 }SSTable;
  //  靜態查詢表(順序表和有序表)的基本操作(7個)
 Status Creat_Seq(SSTable &ST,int n)
 { // 操作結果: 構造一個含n個數據元素的靜態順序查詢表ST(資料來自全域性陣列r)
   int i;
   ST.elem=(ElemType*)calloc(n+1,sizeof(ElemType)); // 動態生成n個數據元素空間(0號單元不用)
   if(!ST.elem)
     return ERROR;
   for(i=1;i<=n;i++)
     *(ST.elem+i)=r[i-1]; // 將全域性陣列r的值依次賦給ST
   ST.length=n;
   return OK;
 }
 void Ascend(SSTable &ST)
 { // 重建靜態查詢表為按關鍵字非降序排序
   int i,j,k;
   for(i=1;i<ST.length;i++)
   {
     k=i;
     ST.elem[0]=ST.elem[i]; // 待比較值存[0]單元
     for(j=i+1;j<=ST.length;j++)
       if LT(ST.elem[j].key,ST.elem[0].key)
       {
         k=j;
         ST.elem[0]=ST.elem[j];
       }
     if(k!=i) // 有更小的值則交換
     {
       ST.elem[k]=ST.elem[i];
       ST.elem[i]=ST.elem[0];
     }
   }
 }
 Status Creat_Ord(SSTable &ST,int n)
 { // 操作結果: 構造一個含n個數據元素的靜態按關鍵字非降序查詢表ST
   // 資料來自全域性陣列r
   Status f;
   f=Creat_Seq(ST,n);
   if(f)
     Ascend(ST);
   return f;
 }

  Status Destroy(SSTable &ST)
 { // 初始條件: 靜態查詢表ST存在。操作結果: 銷燬表ST
   free(ST.elem);
   ST.elem=NULL;
   ST.length=0;
   return OK;
 }

 int Search_Seq(SSTable ST,KeyType key)
 { // 在順序表ST中順序查詢其關鍵字等於key的資料元素。若找到,則函式值為
   // 該元素在表中的位置,否則為0。演算法9.1
   int i;
   ST.elem[0].key=key; // 哨兵
   for(i=ST.length;!EQ(ST.elem[i].key,key);--i); // 從後往前找
   return i; // 找不到時,i為0
 }

 int Search_Bin(SSTable ST,KeyType key)
 { // 在有序表ST中折半查詢其關鍵字等於key的資料元素。若找到,則函式值為
   // 該元素在表中的位置,否則為0。演算法9.2
   int low,high,mid;
   low=1; // 置區間初值
   high=ST.length;
   while(low<=high)
   {
     mid=(low+high)/2;
     if EQ(key,ST.elem[mid].key)  // 找到待查元素
       return mid;
     else if LT(key,ST.elem[mid].key)
       high=mid-1; // 繼續在前半區間進行查詢
     else
       low=mid+1; // 繼續在後半區間進行查詢
   }
   return 0; // 順序表中不存在待查元素
 }

 Status Traverse(SSTable ST,void(*Visit)(ElemType))
 {      // 初始條件: 靜態查詢表ST存在,Visit()是對元素操作的應用函式
        // 操作結果: 按順序對ST的每個元素呼叫函式Visit()一次且僅一次。
       // 一旦Visit()失敗,則操作失敗
   ElemType *p;
   int i;
   p=++ST.elem; // p指向第一個元素
   for(i=1;i<=ST.length;i++)
     Visit(*p++);
   return OK;
 }
  typedef struct BiTNode
 {
   TElemType data;
   BiTNode *lchild,*rchild; // 左右孩子指標
 }BiTNode,*BiTree;
 Status SecondOptimal(BiTree &T, ElemType R[],int sw[],int low,int high)
 { // 由有序表R[low..high]及其累計權值表sw(其中sw[0]==0)遞迴構造
   // 次優查詢樹T。演算法9.3
   int i,j;
   double min,dw;
   i=low;
   min=fabs(sw[high]-sw[low]);
   dw=sw[high]+sw[low-1];
   for(j=low+1;j<=high;++j) // 選擇最小的△Pi值
      if(fabs(dw-sw[j]-sw[j-1])<min)
      {
       i=j;
       min=fabs(dw-sw[j]-sw[j-1]);
     }
   if(!(T=(BiTree)malloc(sizeof(BiTNode))))
     return ERROR;
     
        T->data=R[i]; // 生成結點
   if(i==low)
        T->lchild=NULL; // 左子樹空
   else
        SecondOptimal(T->lchild,R,sw,low,i-1); // 構造左子樹
   if(i==high)
        T->rchild=NULL; // 右子樹空
   else
        SecondOptimal(T->rchild,R,sw,i+1,high); // 構造右子樹
   return OK;
 }
 void FindSW(int sw[],SSTable ST)
 { // 按照有序表ST中各資料元素的Weight域求累計權值表sw
   int i;
   sw[0]=0;
   for(i=1;i<=ST.length;i++)
     sw[i]=sw[i-1]+ST.elem[i].weight;
 }

 typedef BiTree SOSTree; // 次優查詢樹採用二叉連結串列的儲存結構
 Status CreateSOSTree(SOSTree &T,SSTable ST)
 { // 由有序表ST構造一棵次優查詢樹T。ST的資料元素含有權域weight。演算法9.4
   if(ST.length==0)
     T=NULL;
   else
   {
     FindSW(sw,ST); // 按照有序表ST中各資料元素的Weight域求累計權值表sw
     SecondOptimal(T,ST.elem,sw,1,ST.length);
   }
   return OK;
 }

 Status Search_SOSTree(SOSTree &T,KeyType key)
 { // 在次優查詢樹T中查詢關鍵字等於key的元素。找到則返回OK,否則返回FALSE
   while(T) // T非空
     if(T->data.key==key)
       return OK;
     else if(T->data.key>key)
       T=T->lchild;
     else
       T=T->rchild;
   return FALSE; // 順序表中不存在待查元素
 }

 void print(ElemType c) // Traverse()呼叫的函式
 {
   printf("(%c %d) ",c.key,c.weight);
 }

 int main()
 {
   SSTable st;
   SOSTree t;
   Status i;
   KeyType s;
   Creat_Ord(st,N); // 由全域性陣列產生非降序靜態查詢表st
   Traverse(st,print);
   CreateSOSTree(t,st); // 由有序表構造一棵次優查詢樹
   printf("\n請輸入待查詢的字元: ");
   scanf("%c",&s);
   i=Search_SOSTree(t,s);
   if(i)
     printf("%c的權值是%d\n",s,t->data.weight);
   else
     printf("表中不存在此字元\n");
 }