搜索算法—二叉搜索樹


1.什么是二叉搜索樹(BinarySearchTrees)

  如下圖所示:15為樹的根節點,10為15的左節點,20為15的右節點,下面的節點如此類推。

  每個父節點都有兩個子節點(子節點可能為空),左子節點比父節點小,右子節點比父節點大。

2.二叉搜索樹的各種功能

一、節點

  每個節點應該含有兩個子節點,一個可以進行比較的key(本文使用的是int)。節點可以根據需求來含有其它附屬內容,本文為了方便測試,節點含有一個String和記錄該節點下面共有多少個節點(包括自己)的Count。

  節點實現:

UCLASS()
class ALGORITHM_API ATreeNode : public AActor
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    ATreeNode();
    // Called every frame
    virtual void Tick(float DeltaTime) override;
    //設值
    FORCEINLINE void SetValue(int Newkey, FString NewValue)
    {
        Key = Newkey;
        Value = NewValue;
    }

    FORCEINLINE ATreeNode* Get() { return this; }
    //獲取或修改私有變量
    FORCEINLINE int GetKey() { return Key; }
    FORCEINLINE void SetKey(int NewKey) { Key = NewKey; }
    FORCEINLINE int GetCount() { return Count; }
    FORCEINLINE void SetCount(int NewCount) { Count = NewCount; }
    FORCEINLINE FString GetValue() { return Value; }
    FORCEINLINE void SetValue(FString NewValue) { Value = NewValue; }
    FORCEINLINE ATreeNode* GetNode(bool Left)
    {
        if (Left) return LeftNode;
        return RightNode;
    }
    FORCEINLINE void SetNode(bool Left, ATreeNode* NewNode)
    {
        if (Left)  LeftNode = NewNode;
        else
        {
            RightNode = NewNode;
        }
    }
protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
private:
    int Key;
    FString Value;
    //左右節點
    ATreeNode* LeftNode;
    ATreeNode* RightNode;
    //計算此節點下面共用多少個節點(包括自己)
    int Count;
};

二、查找

  如果想查找某個數字X,從根節點開始比較。如果X比根節點大,則去與根節點的右節點比較,如此類推,直到找到X(或子節點為空)為止。

  例如上圖二叉搜索樹中,如果想找25:

  從根節點開始比較:25>15。去根節點右邊,與下一個節點(20)比較,結果25>20。接着去右邊,與下一個節點(25)比較,結果25=25,查找成功。

  代碼實現:

FString ABinarySearchTrees::GetValue(int InputKey)
{
    ATreeNode* X = RootNode;
    while (X != nullptr)
    {
        //比較key的大小
        int Temp = CompareTo(InputKey, X->GetKey());
        //如果輸入的key比X的小,去X的左邊
        if (Temp < 0) X = X->GetNode(true);
        //如果輸入的key比X的大,去X的右邊
        else if (Temp > 0) X = X->GetNode(false);
        //如果相等,說明找到這個key了,輸出Value
        else return X->GetValue();
    }
    //如果X為空指針,說明找不到這個key
    return "NotFind";
}

三、插入新節點

  如果新節點的key在樹中已經存在,則把舊節點覆蓋;如果沒有,則插入。

  與查找類似,設新節點的key為X,從根節點開始比較。如果X比根節點大,則去與根節點的右節點比較。如此類推,如果找到了某個節點的key與X相同,覆蓋這個節點;如果沒找到,則根據最后一次比較結果,插到最后一次比較的節點的左節點或右節點。

  

實現代碼:

void ABinarySearchTrees::Put(int Newkey)
{
    //每次插入前,都清空路線信息
    RouteString = "";
    //每次插入新節點都要更新根節點
    RootNode = Put(RootNode, Newkey);
    RootNode->SetValue(": Root");
    
}

ATreeNode* ABinarySearchTrees::Put(ATreeNode* X, int NewKey)
{
    //如果X不存在,創造一個
    if (!X)
    {
        ATreeNode* NewNode = GetWorld()->SpawnActor<ATreeNode>(ATreeNode::StaticClass());
        NewNode->SetValue(NewKey, RouteString);
        //節點存起來,方便測試
        NodeArray.Add(NewNode);
        //新節點自己算一個節點
        NewNode->SetCount(1);
        return NewNode;
    }
    int Temp = CompareTo(NewKey, X->GetKey());
    //如果要插入新節點,則新節點的所有父節點都要更新一次
    if (Temp < 0)
    {
        RouteString.Append(": Left");
        X->SetNode(true, Put(X->GetNode(true), NewKey));
    }
    else if (Temp > 0)
    {
        RouteString.Append(": Right");
        X->SetNode(false, Put(X->GetNode(false), NewKey));
    }
    else
    {
        X->SetValue(RouteString);
    }
    //更新X節點的Count
    X->SetCount(1 + Size(X->GetNode(true)) + Size(X->GetNode(false)));
    return X;
}

 

四、查找最小值、最大值

  查找最小值:因為二叉搜索樹中,左節點比父節點小,故最小值肯定在樹的左下角。從根節點開始,判斷它的左節點存不存在。如果存在,繼續找這個左節點的左節點,如此類推,直到找到某個節點的左節點不存在時,此節點就是最小值。

  查找最大值:因為二叉搜索樹中,右節點比父節點大,故最大值肯定在樹的右下角。從根節點開始,判斷它的右節點存不存在。如果存在,繼續找這個右節點的右節點,如此類推,直到找到某個節點的右節點不存在時,此節點就是最大值。

 

  實現代碼:

//尋找最小值
int ABinarySearchTrees::FindMin()
{
    //從根節點開始比較
    ATreeNode* X = FindMin(RootNode);
    if (X) return X->GetKey();
    return 0;
}

//尋找擁有最小值的節點
ATreeNode* ABinarySearchTrees::FindMin(ATreeNode* X)
{
    //當節點存在時
    while (X)
    {
        //如果右節點存在,繼續循環
        if (X->GetNode(true))
        {
            X = X->GetNode(true);
        }
        //如果右節點不存在,這個節點就是最小值
        else
        {
            return X;
        }
    }
    return X;
}

int ABinarySearchTrees::FindMax()
{
    //從根節點開始比較
    ATreeNode* X = FindMax(RootNode);
    if (X) return X->GetKey();
    return 0;
}
//尋找擁有最大值的節點
ATreeNode* ABinarySearchTrees::FindMax(ATreeNode* X)
{
    //當節點存在時
    while (X)
    {
        //如果右節點存在,繼續循環
        if (X->GetNode(false))
        {
            X = X->GetNode(false);
        }
        //如果右節點不存在,這個節點就是最小值
        else
        {
            return X;
        }
    }
    return X;
}

 

五、給定一個key,尋找最接近它的節點

  Floor:最接近此key,且小於等於它的節點。

  Ceiling:最接近此key,且大於等於它的節點。

  尋找Floor:從下面的例子中介紹思路

  假設給定key為X,現有搜索樹如下圖

假設X=13:

  1.從根節點開始比較,X比19小,去19節點的左邊。

  2.X>10,但由於最接近X的節點可能在10的右節點下面,故還需去10的右邊繼續找。

  3.X<14, 去14的左邊。

  4.X>12,且12沒子節點,故12就是X的Floor。

假設X=11:

  1.從根節點開始比較,X比19小,去19節點的左邊。

  2.X>10,但由於最接近X的節點可能在10的右節點下面,故還需去10的右邊繼續找。

  3.X<14, 去14的左邊。

  4.X<12,且12沒左子節點,故10的右節點下面沒有比X小的節點,10就是X的Floor。

思路:

  1.X與根節點比較,如果X小,去根節點的左節點,繼續比較,直到找到一個比X小的節點A為止。

  2.去A節點的右節點找比X小的節點。如果找不到,A就是要找的節點;如果找到了,令此節點為B,判斷B節點是否有右節點,如果有,重復步驟2;如果沒有,B節點就是要找的節點。

尋找Ceiling的過程與Floor類似,在此不累贅。

實現代碼:

//給定一個數字,尋找最接近它的key(比它小)
int ABinarySearchTrees::FindFloor(int InputKey)
{
    //從根節點開始比較
    ATreeNode* X = FindFloor(RootNode,InputKey);
    if (X) return X->GetKey();
    return 0;
}

ATreeNode* ABinarySearchTrees::FindFloor(ATreeNode* X, int InputKey)
{
    //如果X節點不存在,就別繼續下去了
    if (!X) return nullptr;
    int Temp = CompareTo(InputKey, X->GetKey());
    //如果存在節點的key與輸入值相等,則這個節點就是最接近它了
    if (Temp == 0) return X;
    //如果節點的key比較大,則去找它的左節點,直到找到小於等於輸入值的節點為止
    if (Temp < 0) return FindFloor(X->GetNode(true), InputKey);
    //如果節點的key比較小,則要找的節點可能在它的右節點的左端
    ATreeNode* T = FindFloor(X->GetNode(false), InputKey);
    //如果找到了T,則說明找到了,返回T;如果找不到,說明X已經是最接近的了,返回X
    if (T) return T;
    else return X;
}

//給定一個數字,尋找最接近它的key(比它大)
int ABinarySearchTrees::FindCeiling(int InputKey)
{
    //從根節點開始比較
    ATreeNode* X = FindCeiling(RootNode, InputKey);
    if (X) return X->GetKey();
    return 0;
}

ATreeNode* ABinarySearchTrees::FindCeiling(ATreeNode* X, int InputKey)
{
    //如果X節點不存在,就別繼續下去了
    if (!X) return nullptr;
    int Temp = CompareTo(InputKey, X->GetKey());
    //如果存在節點的key與輸入值相等,則這個節點就是最接近它了
    if (Temp == 0) return X;
    //如果節點的key比較小,則去找它的右節點,直到找到大於等於輸入值的節點為止
    if (Temp > 0) return FindCeiling(X->GetNode(false), InputKey);
    //如果節點的key比較大,則要找的節點可能在它的左節點的左端
    ATreeNode* T = FindCeiling(X->GetNode(true), InputKey);
    //如果找到了T,則說明找到了,返回T;如果找不到,說明X已經是最接近的了,返回X
    if (T) return T;
    else return X;
}

 六、給定一個keyX,求有多少個節點的key比X小(即求Rank(X))

  這里就會用到節點里的Count了。這個Count記錄了該節點下面共有多少個節點(包括自己)。

  從例子介紹思路:

  假設給定key為X,現有搜索樹如下圖

  

  假設X=13,令根節點為A節點

  1.從A節點開始比較,X比19小,去19節點的左邊。

  2.X>10,此時10和10左節點以下的所有節點都比X小,因為10的右節點下面可能還有節點比K小,所以還需要繼續找,假設還有Y個節點在下面,則Rank(X)=1+4.Count+Y

  3.X<14, 去14的左邊。

  4.X>12,且12沒子節點,故Y=12.Count=1,Rank(X)=1+4.Count+Y=1+3+1=5。

思路:

  令根節點為A節點

  1. 從A節點開始比較:

  如果X<A.key,則如果A的左節點存在,令A的左節點為A,重復步驟1;如果不存在,返回0;

  如果X=A.key,則由於A的右節點下方都比A大,A的左節點下方都比A小,所以Rank(X)=A的左節點的Count;

  如果X>A.key,則Rank(X)=1 + A的左節點的Count + A的右節點下方小於X的節點個數,進入步驟2。

  2. 令A的右節點為A,重復步驟1。

  3. 此循環結束后,便得到Rank(X)。

實現代碼:  

//求有多少個數字少於給定數字
int ABinarySearchTrees::Rank(int InputKey)
{
    return Rank(InputKey, RootNode);
    
}

int ABinarySearchTrees::Rank(int InputKey, ATreeNode* X)
{
    //如果節點不存在,返回0
    if (!X) return 0;
    int Temp = CompareTo(InputKey, X->GetKey());
    //如果給定數字比X的key小,則去X的左邊去找比給定數字小的數字
    if (Temp < 0) return Rank(InputKey, X->GetNode(true));
    //如果給定數字比X的key大,則X和X的左節點都比給定數字小,把它們算上后,去X的右節點找是否還有比給定數字小的數字
    else if (Temp > 0) return 1 + Size(X->GetNode(true)) + Rank(InputKey, X->GetNode(false));
    //因為右節點都比X大,而X的Key與給定數字相等,故比給定數字小的數字都在X的左節點里
    else return Size(X->GetNode(true));
}

 

七、中序排列  

  通過對二叉搜索樹進行中序排列,可以得到一個遞增的有序數組。對某個節點進行中序排列就是先排此節點的左節點,再排這個節點,最后排此節點的右節點。

  例如對下圖的節點4進行中序排列:2,4,7。

  

  如果要對一個大節點排序,如下圖:

  對節點10進行中序排列:需要先對節點4進行中序排列,然后排10,再對節點14進行中序排列

  同樣道理,對根節點排序(即整個二叉搜索樹進行中序排列):  

  對節點19進行中序排列:需要先對節點10進行中序排列,然后排19,再對節點25進行中序排列

實現代碼:

void ABinarySearchTrees::InorderTraversal()
{
    OrderNodeArray.Empty();
    Inorder(RootNode);
}

void ABinarySearchTrees::Inorder(ATreeNode* X)
{
    if (!X) return;
    //先去加X的左節點
    Inorder(X->GetNode(true));
    //再加X
    OrderNodeArray.Add(X->GetKey());
    //最后加X的右節點
    Inorder(X->GetNode(false));
}

 八、刪除節點

  要刪除一個節點,可能會遇到3種情況:

  A. 這個節點沒子節點,此情況最為簡單,直接刪除這個節點即可。

  B. 這個節點只有一個子節點,把這個子節點和這個節點的父節點相連,然后刪除這個節點

  C. 這個節點有兩個子節點,此情況最為復雜,稍后討論

舉個例子:

  

  如果要刪除節點2,則就是A情況,直接刪除即可;

  如果要刪除節點4,則就是B情況,由於4節點是10節點的左節點,故把節點2放在節點10的左節點處,然后刪除節點4。

  如果要刪除節點10,則就是C情況,首先,我們需要從節點10下面找一個節點X來代替它,如下圖所示:

  可以看出X肯定比4大,比14小。故X只能從14的下面去找。顯然,在此例子中X=12。替代了后,我們當然需要把14的左節點刪掉,替換效果:

  為了更直觀的討論,我們不妨將此樹拓展一下:

  刪除節點10后:

  此時,X肯定比4大,比14小,X的候選者有11,12,13。

  不能選12,因為12有兩個節點,處理起來過於復雜,且會破壞二叉搜索樹的結構

  11可以選,處理起來最簡單。

  不能選13,因為如果X=13,就會破壞了二叉搜索樹的結構,如下圖:

  在二叉搜索樹的結構中,父節點應該比它的右節點下面的所有節點都小。而此例子中,13>12>11。

  選擇11時,X=11,刪除節點成功,如下圖

 

  因此,回到情況C:這個節點有兩個子節點,要刪除它的操作是:

  1. 從此節點的右節點下面找出最小的節點X。

  2. 因為X節點是最小的,所以它的子節點數量肯定小於等於1個,把此子節點連到X的父節點上。

  3. 然后用X節點替換要刪除的節點。

  4. 刪除要刪除的節點。

  至於怎么刪除最小節點,上面已經介紹了如何找到最小節點,要刪除它只需在此基礎上進行少量操作。

實現代碼:

//刪除最小的節點
void ABinarySearchTrees::DeleteMin()
{
    RootNode = DeleteMin(RootNode);
}

ATreeNode* ABinarySearchTrees::DeleteMin(ATreeNode* X)
{
    //如果X的左節點不存在,說明已經找到最小值了,刪除它,返回它的右節點
    if (!X->GetNode(true))
    {
        ATreeNode* TempNode = X->GetNode(false);
        NodeArray.Remove(X);
        X->Destroy();
        return TempNode;
    }
    //刪除最小值后,把它的右節點和上一個節點連上
    X->SetNode(true, DeleteMin(X->GetNode(true)));
    //更新節點數
    X->SetCount(1 + Size(X->GetNode(true)) + Size(X->GetNode(false)));
    return X;
}

void ABinarySearchTrees::Delete(int InputKey)
{
    RootNode = Delete(RootNode, InputKey);
}

ATreeNode* ABinarySearchTrees::Delete(ATreeNode* X, int InputKey)
{
    
    if (!X) return nullptr;
    int Temp = CompareTo(InputKey, X->GetKey());
    //尋找要刪除的節點,只要刪了一個節點,它上面的所有節點都要更新一次,所以是SetNode
    if (Temp < 0) X->SetNode(true, Delete(X->GetNode(true), InputKey));
    else if (Temp > 0) X->SetNode(false, Delete(X->GetNode(false), InputKey));
    //找到要刪除的節點了
    else
    {
        //如果要刪除的節點只有一個子節點或沒有,那好辦,只需把那個子節點替代它就好
        if (!X->GetNode(false))
        {
            ATreeNode* TempNode = X->GetNode(true);
            NodeArray.Remove(X);
            X->Destroy();
            return TempNode;
        }
        if (!X->GetNode(true))
        {
            ATreeNode* TempNode = X->GetNode(false);
            NodeArray.Remove(X);
            X->Destroy();
            return TempNode;
        }
        
        //如果要刪除的節點有兩個個子節點,從它的右邊找一個最小的節點來替代它
        ATreeNode* T = X;
        X = FindMin(T->GetNode(false));
        X->SetNode(false, DeleteMin(T->GetNode(false)));
        X->SetNode(true, T->GetNode(true));
        NodeArray.Remove(T);
        T->Destroy();
    }
    //更新節點數
    X->SetCount(Size(X->GetNode(true)) + Size(X->GetNode(false)) + 1);
    return X;
    
}

 

九、完整的全部代碼

節點.h:

UCLASS()
class ALGORITHM_API ATreeNode : public AActor
{
    GENERATED_BODY()
    
public:    
    // Sets default values for this actor's properties
    ATreeNode();
    // Called every frame
    virtual void Tick(float DeltaTime) override;
    //設值
    FORCEINLINE void SetValue(int Newkey, FString NewValue)
    {
        Key = Newkey;
        Value = NewValue;
    }

    FORCEINLINE ATreeNode* Get() { return this; }
    //獲取或修改私有變量
    FORCEINLINE int GetKey() { return Key; }
    FORCEINLINE void SetKey(int NewKey) { Key = NewKey; }
    FORCEINLINE int GetCount() { return Count; }
    FORCEINLINE void SetCount(int NewCount) { Count = NewCount; }
    FORCEINLINE FString GetValue() { return Value; }
    FORCEINLINE void SetValue(FString NewValue) { Value = NewValue; }
    FORCEINLINE ATreeNode* GetNode(bool Left)
    {
        if (Left) return LeftNode;
        return RightNode;
    }
    FORCEINLINE void SetNode(bool Left, ATreeNode* NewNode)
    {
        if (Left)  LeftNode = NewNode;
        else
        {
            RightNode = NewNode;
        }
    }
protected:
    // Called when the game starts or when spawned
    virtual void BeginPlay() override;
private:
    int Key;
    FString Value;
    //左右節點
    ATreeNode* LeftNode;
    ATreeNode* RightNode;
    //計算此節點下面共用多少個節點(包括自己)
    int Count;
};

二叉搜索樹.h:
class ATreeNode; UCLASS() class ALGORITHM_API ABinarySearchTrees : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties ABinarySearchTrees(); // Called every frame virtual void Tick(float DeltaTime) override; //查值 FString GetValue(int InputKey); //某個key是否存在? bool Search(int InputKey); //插入一個節點 void Put(int Newkey); ATreeNode* Put(ATreeNode* X, int NewKey); //提供一個方法讓TreeNode之間進行比較 //如果a大於b,返回1;如果a小於b,返回-1;如果相等,返回0 int CompareTo(int a, int b); //尋找最小值 int FindMin(); //尋找擁有最小值的節點 ATreeNode* FindMin(ATreeNode* X); //尋找最大值 int FindMax(); //尋找擁有最大值的節點 ATreeNode* FindMax(ATreeNode* X); //給定一個數字,尋找最接近它的key(比它小) int FindFloor(int InputKey); ATreeNode* FindFloor(ATreeNode* X, int InputKey); //給定一個數字,尋找最接近它的key(比它大) int FindCeiling(int InputKey); ATreeNode* FindCeiling(ATreeNode* X, int InputKey); //求有多少個數字少於給定數字 int Size(ATreeNode* X); int Rank(int InputKey); int Rank(int InputKey, ATreeNode* X); //中序遍歷 void InorderTraversal(); void Inorder(ATreeNode* X); //刪除最小值 void DeleteMin(); ATreeNode* DeleteMin(ATreeNode* X); //刪除某個節點 void Delete(int InputKey); ATreeNode* Delete(ATreeNode* X, int InputKey); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; public: private: //根節點 ATreeNode* RootNode; //節點數組 TArray<ATreeNode*> NodeArray; //把節點接過的路線記錄下來,方便測試 FString RouteString; //把節點按中序遍歷放進數組 TArray<int> OrderNodeArray; }; 二叉搜索樹.cpp: // Sets default values ABinarySearchTrees::ABinarySearchTrees() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; } // Called when the game starts or when spawned void ABinarySearchTrees::BeginPlay() { Super::BeginPlay(); FRandomStream Stream; Stream.GenerateNewSeed(); //生成節點 for (int i = 0; i < 100; i++) { Put(Stream.RandRange(0, 100)); } Put(40); //測試二叉搜索樹是否正確 for (int i = 0; i < NodeArray.Num(); i++) { UKismetSystemLibrary::PrintString(this, FString::FromInt(NodeArray[i]->GetKey())+ NodeArray[i]->GetValue()+" Count: "+ FString::FromInt(NodeArray[i]->GetCount())); } UKismetSystemLibrary::PrintString(this, FString::FromInt(Rank(49))); //測試搜索和查值功能 if (Search(40)) { UKismetSystemLibrary::PrintString(this, "Find 40: " + GetValue(40)); } //測試尋找最小值、最大值、Floor、Ceiling UKismetSystemLibrary::PrintString(this, "Min: " + FString::FromInt(FindMin()) + " Max: " + FString::FromInt(FindMax())); UKismetSystemLibrary::PrintString(this, "Floor of 50: " + FString::FromInt(FindFloor(50))); UKismetSystemLibrary::PrintString(this, "Ceiling of 50: " + FString::FromInt(FindCeiling(50))); //測試刪除 Delete(40); //測試中序排序 InorderTraversal(); for (int i = 0; i < OrderNodeArray.Num(); i++) { UKismetSystemLibrary::PrintString(this, FString::FromInt(OrderNodeArray[i])); } for (int i = 0; i < NodeArray.Num(); i++) { if (!NodeArray[i]) return; UKismetSystemLibrary::PrintString(this, FString::FromInt(NodeArray[i]->GetKey()) + NodeArray[i]->GetValue() + " Count: " + FString::FromInt(NodeArray[i]->GetCount())); } } // Called every frame void ABinarySearchTrees::Tick(float DeltaTime) { Super::Tick(DeltaTime); } FString ABinarySearchTrees::GetValue(int InputKey) { ATreeNode* X = RootNode; while (X != nullptr) { //比較key的大小 int Temp = CompareTo(InputKey, X->GetKey()); //如果輸入的key比X的小,去X的左邊 if (Temp < 0) X = X->GetNode(true); //如果輸入的key比X的大,去X的右邊 else if (Temp > 0) X = X->GetNode(false); //如果相等,說明找到這個key了,輸出Value else return X->GetValue(); } //如果X為空指針,說明找不到這個key return "NotFind"; } //某個key是否存在? bool ABinarySearchTrees::Search(int InputKey) { ATreeNode* X = RootNode; while (X != nullptr) { //比較key的大小 int Temp = CompareTo(InputKey, X->GetKey()); //如果輸入的key比X的小,去X的左邊 if (Temp < 0) X = X->GetNode(true); //如果輸入的key比X的大,去X的右邊 else if (Temp > 0) X = X->GetNode(false); //如果相等,說明找到這個key了 else return true; } //如果X為空指針,說明找不到這個key return false; } void ABinarySearchTrees::Put(int Newkey) { //每次插入前,都清空路線信息 RouteString = ""; //每次插入新節點都要更新根節點 RootNode = Put(RootNode, Newkey); RootNode->SetValue(": Root"); } ATreeNode* ABinarySearchTrees::Put(ATreeNode* X, int NewKey) { //如果X不存在,創造一個 if (!X) { ATreeNode* NewNode = GetWorld()->SpawnActor<ATreeNode>(ATreeNode::StaticClass()); NewNode->SetValue(NewKey, RouteString); //節點存起來,方便測試 NodeArray.Add(NewNode); //新節點自己算一個節點 NewNode->SetCount(1); return NewNode; } int Temp = CompareTo(NewKey, X->GetKey()); //如果要插入新節點,則新節點的所有父節點都要更新一次 if (Temp < 0) { RouteString.Append(": Left"); X->SetNode(true, Put(X->GetNode(true), NewKey)); } else if (Temp > 0) { RouteString.Append(": Right"); X->SetNode(false, Put(X->GetNode(false), NewKey)); } else { X->SetValue(RouteString); } //更新X節點的Count X->SetCount(1 + Size(X->GetNode(true)) + Size(X->GetNode(false))); return X; } //如果a大於b,返回1;如果a小於b,返回-1;如果相等,返回0 int ABinarySearchTrees::CompareTo(int a, int b) { if (a > b) { return 1; } else if (a < b) { return -1; } else { return 0; } } //尋找最小值 int ABinarySearchTrees::FindMin() { //從根節點開始比較 ATreeNode* X = FindMin(RootNode); if (X) return X->GetKey(); return 0; } //尋找擁有最小值的節點 ATreeNode* ABinarySearchTrees::FindMin(ATreeNode* X) { //當節點存在時 while (X) { //如果左節點存在,繼續循環 if (X->GetNode(true)) { X = X->GetNode(true); } //如果右節點不存在,這個節點就是最小值 else { return X; } } return X; } int ABinarySearchTrees::FindMax() { //從根節點開始比較 ATreeNode* X = FindMax(RootNode); if (X) return X->GetKey(); return 0; } //尋找擁有最大值的節點 ATreeNode* ABinarySearchTrees::FindMax(ATreeNode* X) { //當節點存在時 while (X) { //如果右節點存在,繼續循環 if (X->GetNode(false)) { X = X->GetNode(false); } //如果右節點不存在,這個節點就是最小值 else { return X; } } return X; } //給定一個數字,尋找最接近它的key(比它小) int ABinarySearchTrees::FindFloor(int InputKey) { //從根節點開始比較 ATreeNode* X = FindFloor(RootNode,InputKey); if (X) return X->GetKey(); return 0; } ATreeNode* ABinarySearchTrees::FindFloor(ATreeNode* X, int InputKey) { //如果X節點不存在,就別繼續下去了 if (!X) return nullptr; int Temp = CompareTo(InputKey, X->GetKey()); //如果存在節點的key與輸入值相等,則這個節點就是最接近它了 if (Temp == 0) return X; //如果節點的key比較大,則去找它的左節點,直到找到小於等於輸入值的節點為止 if (Temp < 0) return FindFloor(X->GetNode(true), InputKey); //如果節點的key比較小,則要找的節點可能在它的右節點的左端 ATreeNode* T = FindFloor(X->GetNode(false), InputKey); //如果找到了T,則說明找到了,返回T;如果找不到,說明X已經是最接近的了,返回X if (T) return T; else return X; } //給定一個數字,尋找最接近它的key(比它大) int ABinarySearchTrees::FindCeiling(int InputKey) { //從根節點開始比較 ATreeNode* X = FindCeiling(RootNode, InputKey); if (X) return X->GetKey(); return 0; } ATreeNode* ABinarySearchTrees::FindCeiling(ATreeNode* X, int InputKey) { //如果X節點不存在,就別繼續下去了 if (!X) return nullptr; int Temp = CompareTo(InputKey, X->GetKey()); //如果存在節點的key與輸入值相等,則這個節點就是最接近它了 if (Temp == 0) return X; //如果節點的key比較小,則去找它的右節點,直到找到大於等於輸入值的節點為止 if (Temp > 0) return FindCeiling(X->GetNode(false), InputKey); //如果節點的key比較大,則要找的節點可能在它的左節點的左端 ATreeNode* T = FindCeiling(X->GetNode(true), InputKey); //如果找到了T,則說明找到了,返回T;如果找不到,說明X已經是最接近的了,返回X if (T) return T; else return X; } int ABinarySearchTrees::Size(ATreeNode* X) { //如果節點不存在,返回0 if (!X) return 0; //如果節點存在,返回Count return X->GetCount(); } //求有多少個數字少於給定數字 int ABinarySearchTrees::Rank(int InputKey) { return Rank(InputKey, RootNode); } int ABinarySearchTrees::Rank(int InputKey, ATreeNode* X) { //如果節點不存在,返回0 if (!X) return 0; int Temp = CompareTo(InputKey, X->GetKey()); //如果給定數字比X的key小,則去X的左邊去找比給定數字小的數字 if (Temp < 0) return Rank(InputKey, X->GetNode(true)); //如果給定數字比X的key大,則X和X的左節點都比給定數字小,把它們算上后,去X的右節點找是否還有比給定數字小的數字 else if (Temp > 0) return 1 + Size(X->GetNode(true)) + Rank(InputKey, X->GetNode(false)); //因為右節點都比X大,而X的Key與給定數字相等,故比給定數字小的數字都在X的左節點里 else return Size(X->GetNode(true)); } void ABinarySearchTrees::InorderTraversal() { OrderNodeArray.Empty(); Inorder(RootNode); } void ABinarySearchTrees::Inorder(ATreeNode* X) { if (!X) return; //先去加X的左節點 Inorder(X->GetNode(true)); //再加X OrderNodeArray.Add(X->GetKey()); //最后加X的右節點 Inorder(X->GetNode(false)); } //刪除最小的節點 void ABinarySearchTrees::DeleteMin() { RootNode = DeleteMin(RootNode); } ATreeNode* ABinarySearchTrees::DeleteMin(ATreeNode* X) { //如果X的左節點不存在,說明已經找到最小值了,刪除它,返回它的右節點 if (!X->GetNode(true)) { ATreeNode* TempNode = X->GetNode(false); NodeArray.Remove(X); X->Destroy(); return TempNode; } //刪除最小值后,把它的右節點和上一個節點連上 X->SetNode(true, DeleteMin(X->GetNode(true))); //更新節點數 X->SetCount(1 + Size(X->GetNode(true)) + Size(X->GetNode(false))); return X; } void ABinarySearchTrees::Delete(int InputKey) { RootNode = Delete(RootNode, InputKey); } ATreeNode* ABinarySearchTrees::Delete(ATreeNode* X, int InputKey) { if (!X) return nullptr; int Temp = CompareTo(InputKey, X->GetKey()); //尋找要刪除的節點,只要刪了一個節點,它上面的所有節點都要更新一次,所以是SetNode if (Temp < 0) X->SetNode(true, Delete(X->GetNode(true), InputKey)); else if (Temp > 0) X->SetNode(false, Delete(X->GetNode(false), InputKey)); //找到要刪除的節點了 else { //如果要刪除的節點只有一個子節點或沒有,那好辦,只需把那個子節點替代它就好 if (!X->GetNode(false)) { ATreeNode* TempNode = X->GetNode(true); NodeArray.Remove(X); X->Destroy(); return TempNode; } if (!X->GetNode(true)) { ATreeNode* TempNode = X->GetNode(false); NodeArray.Remove(X); X->Destroy(); return TempNode; } //如果要刪除的節點有兩個個子節點,從它的右邊找一個最小的節點來替代它 ATreeNode* T = X; X = FindMin(T->GetNode(false)); X->SetNode(false, DeleteMin(T->GetNode(false))); X->SetNode(true, T->GetNode(true)); NodeArray.Remove(T); T->Destroy(); } //更新節點數 X->SetCount(Size(X->GetNode(true)) + Size(X->GetNode(false)) + 1); return X; }

 

  


免責聲明!

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



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