次優查找樹的建立


  查找效率最高即平均查找長度最小,根據前面所學知識,我們可以給出有序表在非等概率情況下應遵循的兩個原則:   

  1、最先訪問的結點應是訪問概率最大的結點; 

  2、每次訪問應使結點兩邊尚未訪問的結點的被訪概率之和盡可能相等。

  這兩個原則可用一句話來表示,即判定樹為帶權內路徑長度之和最小的二叉樹,亦即:PH = ∑wihi  最小,其中 n 為有序表長度,hi 為第 i 個結點在判定樹上的層次數,wi = cpi,c 為某個常數,pi 為第 i 個結點的查找概率。

        這樣的樹稱為靜態最優查找樹(static optimal search tree),構造這樣一棵樹的時間代價太大,亦即時間復雜度很大,因此我們通常是構造次優查找樹(nearly optimal search tree),構造它的時間代價遠遠低於構造最優查找樹,但查找性能只比最優查找樹差1%~2%,很少差3%以上。

次優查找樹的構造: 
  
        設有序表每個記錄的權值為 wl,wl+1,…,wh,第一個應訪問的結點號為 i ,則有: 
Δpi =   ∑wj - ∑wj   最小,即 Δpi = Min {Δpj } 
再分別對 {rl,rl+1,…,ri-1} 和 {ri+1,ri+2,…,rh} 分別構造次優查找樹。 
  
為便於計算,引入累計權值swi=∑wj,並設wl-1=swl-1=0,則:

查找構造靜態次優查找樹(SOST) - 薔薇閣 - 落落工作室

    
        由於在構造次優查找樹時沒有考慮前面說的原則一,因此被選為根的結點的權值可能比其鄰近結點的權值小,此時應對查找樹作適當的調整,將相鄰權值大的結點作為根結點。

          次優查找樹的查找方法與折半查找類似,其平均查找長度與 log n 成正比。

注意:利用上述算法構造好次優二叉樹之后,可能並不是最優的,因為在構造過程中,沒有考慮單個關鍵字的相應權值,則有可能出現被選為根的關鍵字的權值比與

    它相鄰的關鍵字的權值小。此時應做適當的調整:選取鄰近的權值較大的關鍵字作為次優查找樹的根節點(也就是左旋和右旋子樹

#include<iostream>
#include
<cstring> #include<cstdio> #include<algorithm> #include<string> #include<cmath> #define N 100 #define MAXN 0x3f3f3f3f using namespace std; template<typename T> class TreeNode{ public: TreeNode* child[2]; T val; int w; TreeNode(){ child[0] = child[1] = NULL; } }; template<typename T> class NearlyOptimalSearchTree{//次優查找樹 public: int n; T val[N]; int w[N]; int sw[N]; TreeNode<T> *t; void input(); void init(); void outT(TreeNode<T>* t); private: void buildT(int ld, int rd, TreeNode<T>* &t);//建立次優查找樹 void adjustment(TreeNode<T>* &t);//調整次優查找樹 void rotateT(TreeNode<T>* &t, int x); }; template<typename T> void NearlyOptimalSearchTree<T>::input(){ cin>>n; for(int i=1; i<=n; ++i) cin>>val[i]>>w[i]; } template<typename T> void NearlyOptimalSearchTree<T>::init(){ sw[0] = 0; for(int i=1; i<=n; ++i) sw[i] = sw[i-1]+w[i]; buildT(1, n, t); cout<<"沒有調整前的先序遍歷:"<<endl; outT(t); adjustment(t); cout<<endl<<"調整后的先序遍歷:"<<endl; outT(t); cout<<endl; } template<typename T> void NearlyOptimalSearchTree<T>::buildT(int ld, int rd, TreeNode<T>* &t){ if(ld > rd) return; int minN = MAXN; int i; for(int j=ld; j<=rd; ++j){ int ans = sw[rd] - sw[j-1] - sw[j]; ans = abs(ans); if(minN > ans){ minN = ans; i = j; } } t = new TreeNode<T>; t->val = val[i]; t->w = w[i]; if(ld==rd) return; buildT(ld, i-1, t->child[0]); buildT(i+1, rd, t->child[1]); } template<typename T> void NearlyOptimalSearchTree<T>::adjustment(TreeNode<T>* &t){ if(!t) return; int lmax = 0, rmax = 0; if(t->child[0]) lmax = t->child[0]->w; if(t->child[1]) rmax = t->child[1]->w; int maxVal = max(lmax, rmax); if(t->w < maxVal){ if(maxVal == lmax){ rotateT(t, 1);//右旋子樹 } else { rotateT(t, 0);//左旋子樹 } } adjustment(t->child[0]); adjustment(t->child[1]); } template<typename T> void NearlyOptimalSearchTree<T>::rotateT(TreeNode<T>* &o, int x){ TreeNode<T>* k = o->child[x^1]; o->child[x^1] = k->child[x]; k->child[x] = o; o = k; } template<typename T> void NearlyOptimalSearchTree<T>::outT(TreeNode<T>* t){ if(!t) return; cout<<t->val<<" "; outT(t->child[0]); outT(t->child[1]); } int main(){ NearlyOptimalSearchTree<string> nost; nost.input(); nost.init(); return 0; }

/*
  演示結果如下:

9
A 1
B 1
C 2
D 5
E 3
F 4
G 4
H 3
I 5
沒有調整前的先序遍歷:
F D B A C E G H I
調整后的先序遍歷:
D C B A F E G I H

 
         

5
A 1
B 30
C 2
D 29
E 2
沒有調整前的先序遍歷:
C B A D E
調整后的先序遍歷:
B A D C E


*/

 

#include<iostream> 
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string> 
#include<iomanip>
#include<cmath>
#include<queue>
#define N 100
#define MAXN 0x3f3f3f3f
using namespace std;

template<typename T>
class TreeNode{
    public:
        TreeNode* child[2];
        T val;
        int w; 
        int d;//距離屏幕左端的寬度 
        TreeNode(){
            child[0] = child[1] = NULL;
        }
};

template<typename T>
class NearlyOptimalSearchTree{//次優查找樹 
    public:
        int n;
        T val[N];
        int w[N];
        int sw[N];
        TreeNode<T> *t;
        void input();
        void init();
        void outT(TreeNode<T>* t);
    private:
        int width;
        int step;
        void buildT(int ld, int rd, TreeNode<T>* &t);//建立次優查找樹 
        void adjustment(TreeNode<T>* &t);//調整次優查找樹 
        void rotateT(TreeNode<T>* &t, int x);
        void widthT(TreeNode<T>* t);//計算每個節點到屏幕左端的距離 
};

template<typename T>
void NearlyOptimalSearchTree<T>::input(){
    cin>>n;
    for(int i=1; i<=n; ++i)
        cin>>val[i]>>w[i];
}

template<typename T>
void NearlyOptimalSearchTree<T>::init(){
    sw[0] = 0;
    width = 0;
    step = 2;
    for(int i=1; i<=n; ++i)    
        sw[i] = sw[i-1]+w[i];
    buildT(1, n, t);
    cout<<"沒有調整前的先序遍歷:"<<endl;
    outT(t);
    adjustment(t);
    cout<<endl<<"調整后的先序遍歷:"<<endl;
    outT(t);
    cout<<endl;
}

template<typename T>
void NearlyOptimalSearchTree<T>::buildT(int ld, int rd, TreeNode<T>* &t){
    if(ld > rd) return;
    int minN = MAXN;
    int i;
    for(int j=ld; j<=rd; ++j){
        int ans = sw[rd] - sw[j-1] - sw[j];    
        ans = abs(ans);
        if(minN > ans){
            minN = ans;
            i = j;
        }
    }
    t = new TreeNode<T>;
    t->val = val[i];
    t->w = w[i];
    if(ld==rd) return;
    buildT(ld, i-1, t->child[0]);
    buildT(i+1, rd, t->child[1]);
}

template<typename T>
void NearlyOptimalSearchTree<T>::adjustment(TreeNode<T>* &t){
    if(!t) return;
    int lmax = 0, rmax = 0;
    if(t->child[0]) lmax = t->child[0]->w;
    if(t->child[1]) rmax = t->child[1]->w;
    int maxVal = max(lmax, rmax);
    if(t->w < maxVal){
        if(maxVal == lmax){
            rotateT(t, 1);//右旋子樹 
        } else {
            rotateT(t, 0);//左旋子樹 
        } 
    }
    adjustment(t->child[0]);
    adjustment(t->child[1]);
}

template<typename T>
void NearlyOptimalSearchTree<T>::rotateT(TreeNode<T>* &o, int x){
    TreeNode<T>* k = o->child[x^1];
    o->child[x^1] = k->child[x];
    k->child[x] = o;
    o = k; 
}

template<typename T>
void NearlyOptimalSearchTree<T>::widthT(TreeNode<T>* t){
    if(!t) return;
    widthT(t->child[0]);
    t->d = width;
    width+=step; 
    widthT(t->child[1]);
}

template<typename T>
void NearlyOptimalSearchTree<T>::outT(TreeNode<T>* t){
    width=0;
     widthT(t);
     queue<TreeNode<T>*> q, qq;
     q.push(t);
     int n=1;//當前層的節點個數 
     int i=1;//當前層第幾個節點 
     int nn=0;//統計下一次的節點的個數
     int pred;//前一個節點距離左屏幕的距離 
     while(!q.empty()){
         TreeNode<T>* tt = q.front();
         q.pop();
         qq.push(tt);
        if(tt != t){//不是根節點, 打印分枝豎線 
             if(i==1){
                 printf("%*s", tt->d, "|");
                 pred = tt->d;
             } else {
                 printf("%*s", tt->d-pred, "|");
                 pred = tt->d;
             }
        }
         //放入孩子節點 
         if(tt->child[0]) q.push(tt->child[0]), ++nn;
         if(tt->child[1]) q.push(tt->child[1]), ++nn;
        ++i; 
        if(i>n){//上一層訪問完畢 
             i=1;
             n = nn;
             nn = 0;
             printf("\n");
             bool first = true;//是否是這一行的第一個節點 
             int ld, rd; 
             while(!qq.empty()){//打印上層節點字符 
                 TreeNode<T>* tt = qq.front();
                 qq.pop();
                 if(first){
                     cout<<setw(tt->d)<<tt->val;
                     pred = tt->d;
                     ld = tt->d;
                     if(tt->child[0])
                         ld = tt->child[0]->d; 
                 } else {
                     cout<<setw(tt->d - pred)<<tt->val;
                     pred = tt->d;
                 }
                first = false;
                if(qq.empty()){//這一層的最后一個節點 
                    rd = tt->d+1;
                    if(tt->child[1])
                        rd = tt->child[1]->d;
                }
             }
             printf("\n");
             if(q.empty()) break;//這是最后一層 
             cout<<setw(ld-1)<<"";
             for(int i=ld; i<=rd; ++i)
                 printf("-") ;
             printf("\n");
         }
     }
}

int main(){
    NearlyOptimalSearchTree<string> nost;
    nost.input();
    nost.init();
    return 0;
}

/*
  //演示結果

9
A 1
B 1
C 2
D 5
E 3
F 4
G 4
H 3
I 5
沒有調整前的先序遍歷:

 
         

F
-------
| |
D G
-------------
| | |
B E H
-----------------
| | |
A C I

 
         

調整后的先序遍歷:

 
         

D
-------
| |
C F
-----------
| | |
B E G
-----------------
| |
A I
------------------
|
H


*/

 


免責聲明!

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



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