搜索算法—二叉搜索樹

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

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

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

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

1、節點

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

  節點實現:測試

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;
};

2、查找

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

  例如上圖二叉搜索樹中,若是想找25:spa

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

  代碼實現:指針

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";
}

3、插入新節點

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

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

  

實現代碼:

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;
}

 

4、查找最小值、最大值

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

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

 

  實現代碼:

//尋找最小值
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;
}

 

5、給定一個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;
}

 6、給定一個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));
}

 

7、中序排列  

  經過對二叉搜索樹進行中序排列,能夠獲得一個遞增的有序數組。對某個節點進行中序排列就是先排此節點的左節點,再排這個節點,最後排此節點的右節點。

  例如對下圖的節點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));
}

 8、刪除節點

  要刪除一個節點,可能會遇到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;
    
}

 

9、完整的所有代碼

節點.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; }
相關文章
相關標籤/搜索