1. 程式人生 > >3、【資料結構】樹形結構之二叉查詢樹

3、【資料結構】樹形結構之二叉查詢樹

一、樹的介紹

1. 樹的定義

    樹是一種資料結構,它是由n(n>=1)個有限節點組成一個具有層次關係的集合。   把它叫做“樹”是因為它看起來像一棵倒掛的樹,也就是說它是根朝上,而葉朝下的。它具有以下的特點:   (1) 每個節點有零個或多個子節點;   (2) 沒有父節點的節點稱為根節點;   (3) 每一個非根節點有且只有一個父節點;   (4) 除了根節點外,每個子節點可以分為多個不相交的子樹。

2. 樹的基本術語

若一個結點有子樹,那麼該結點稱為子樹根的"雙親",子樹的根是該結點的"孩子"。有相同雙親的結點互為"兄弟"。一個結點的所有子樹上的任何結點都是該結點的後裔。從根結點到某個結點的路徑上的所有結點都是該結點的祖先。   結點的度

:結點擁有的子樹的數目。   葉子:度為零的結點。   分支結點:度不為零的結點。   樹的度:樹中結點的最大的度。   層次:根結點的層次為1,其餘結點的層次等於該結點的雙親結點的層次加1。   樹的高度:樹中結點的最大層次。   無序樹:如果樹中結點的各子樹之間的次序是不重要的,可以交換位置。   有序樹:如果樹中結點的各子樹之間的次序是重要的, 不可以交換位置。   森林:0個或多個不相交的樹組成。對森林加上一個根,森林即成為樹;刪去根,樹即成為森林。

二、二叉樹的介紹

1. 二叉樹的定義

    二叉樹是每個節點最多有兩個子樹的樹結構。它有五種基本形態:二叉樹可以是空集;根可以有空的左子樹或右子樹;或者左、右子樹皆為空。

2. 二叉樹的性質

二叉樹有以下幾個性質:   性質1:二叉樹第i層上的結點數目最多為 2{i-1} (i≥1)。   性質2:深度為k的二叉樹至多有2{k}-1個結點(k≥1)。   性質3:包含n個結點的二叉樹的高度至少為log2 (n+1)。   性質4:在任意一棵二叉樹中,若終端結點的個數為n0,度為2的結點數為n2,則n0=n2+1

3. 滿二叉樹,完全二叉樹和二叉查詢樹

3.1 滿二叉樹   定義:高度為h,並且由2{h} –1個結點的二叉樹,被稱為滿二叉樹。 3.2 完全二叉樹   定義:一棵二叉樹中,只有最下面兩層結點的度可以小於2,並且最下一層的葉結點集中在靠左的若干位置上。這樣的二叉樹稱為完全二叉樹。   特點:葉子結點只能出現在最下層和次下層,且最下層的葉子結點集中在樹的左部。顯然,一棵滿二叉樹必定是一棵完全二叉樹,而完全二叉樹未必是滿二叉樹。 3.3 二叉查詢樹   定義:二叉查詢樹, 即二叉查詢樹(Binary Search Tree),又被稱為二叉搜尋樹。設x為二叉查詢樹中的一個結點,x節點包含關鍵字key,節點x的key值記為key[x]。如果y是x的左子樹中的一個結點,則key[y] <= key[x];如果y是x的右子樹的一個結點,則key[y] >= key[x]。 在二叉查詢樹中:   (1) 若任意節點的左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;   (2) 任意節點的右子樹不空,則右子樹上所有結點的值均大於它的根結點的值;   (3) 任意節點的左、右子樹也分別為B樹。   (4) 沒有鍵值相等的節點(no duplicate nodes)。 其具體結構如下圖所示: 在這裡插入圖片描述

    二叉查詢樹的搜尋,從根結點開始,如果查詢的關鍵字與結點的關鍵字相等,那麼就命中;否則,如果查詢關鍵字比結點關鍵字小,就進入左兒子;如果比結點關鍵字大,就進入右兒子;如果左兒子或右兒子的指標為空,則報告找不到相應的關鍵字;   如果二叉查詢樹的所有非葉子結點的左右子樹的結點數目均保持差不多(平衡),那麼二叉查詢樹的搜尋效能逼近二分查詢;但它比連續記憶體空間的二分查詢的優點是,改變二叉查詢樹結構(插入與刪除結點)不需要移動大段的記憶體資料,甚至通常是常數開銷;如: 在這裡插入圖片描述     但二叉查詢樹在經過多次插入與刪除後,有可能導致不同的結構:     右邊也是一個二叉查詢樹,但它的搜尋效能已經是線性的了;同樣的關鍵字集合有可能導致不同的樹結構索引;所以,使用B樹還要考慮儘可能讓二叉查詢樹保持左圖的結構,和避免右圖的結構,也就是所謂的“平衡”問題;     實際使用的二叉查詢樹都是在原二叉查詢樹的基礎上加上平衡演算法,即“平衡二叉樹”;如何保持B樹結點分佈均勻的平衡演算法是平衡二叉樹的關鍵;平衡演算法是一種在二叉查詢樹中插入和刪除結點的策略;

三、二叉查詢樹的實現(C++)

BSTree.h

typedef int DataType;

struct BSTNode
{
    DataType data;
    BSTNode *lchild;
    BSTNode *rchild;
    BSTNode *parent;

    BSTNode(DataType x)
        :data(x), lchild(NULL), rchild(NULL), parent(NULL){};
};


class BSTree {
public:
    BSTree();//建構函式
    ~BSTree();//解構函式
//定義外部介面函式
    //前序遍歷
    void preOrder();
    //中序遍歷
    void inOrder();
    //後序遍歷
    void postOrder();
    //分層遍歷
    void levelOrder();

    //查詢鍵值為x的結點
    BSTNode *searchNode(DataType x);
    //將鍵值為x的結點插入二叉樹
    void insertNode(DataType x);
    //刪除鍵值為x的結點
    void removeNode(DataType x);
    //銷燬二叉樹
    void destroyBSTree();
    //列印二叉樹
    void printBSTree();

    //查詢鍵值最小的結點
    DataType minMem();
    //查詢鍵值最大的結點
    DataType maxMem();

private://定義內部資料和介面函式
    BSTNode *root;    // 根結點

    // 前序遍歷"二叉樹"
    void preOrder(BSTNode *root);
    // 中序遍歷"二叉樹"
    void inOrder(BSTNode *root);
    // 後序遍歷"二叉樹"
    void postOrder(BSTNode *root);
    //分層遍歷
    void levelOrder(BSTNode *root);

    //查詢
    BSTNode * searchNode(BSTNode *root, DataType key);
    //插入
    void insertNode(BSTNode * &root, BSTNode *x);
    //刪除
    BSTNode * removeNode(BSTNode *root, BSTNode *x);
    //銷燬
    void destroyBSTree(BSTNode *root);

    //列印
    void printBSTree(BSTNode *root, DataType data, int direction);

    //查詢最小結點
    BSTNode * minMem(BSTNode *root);
    //查詢最大結點
    BSTNode * maxMem(BSTNode *root);
    //查詢前驅
    BSTNode * predecessorode(BSTNode *x);
    //查詢後繼
    BSTNode * successorNode(BSTNode *x);

};

BSTree.cpp

#include <iostream>
#include <iomanip>
#include <queue>
#include "BSTree.h"

using namespace std;

//建構函式
BSTree::BSTree()
    :root(NULL)
{

}

//解構函式
BSTree::~BSTree()
{
    destroyBSTree();
}
//銷燬二叉樹 - 內部函式
void BSTree::destroyBSTree(BSTNode *bstnode)
{
    if(bstnode == NULL)
        return;
    if(bstnode->lchild != NULL)
        return destroyBSTree(bstnode->lchild);
    if(bstnode->rchild != NULL)
        return destroyBSTree(bstnode->rchild);
    delete bstnode;
    bstnode = NULL;
}
//銷燬二叉樹 - 外部函式
void BSTree::destroyBSTree()
{
    destroyBSTree(root);
}

//插入新結點 - 內部函式
void BSTree::insertNode(BSTNode * &bstnode, BSTNode *x)
{
    BSTNode *y = NULL;
    BSTNode *z = bstnode;

    //查詢x的插入位置
    while(z != NULL)
    {
        y = z;
        if(x->data < z->data)
            z = z->lchild;
        else
            z = z->rchild;
    }
    x->parent = y;

    if(y == NULL)
    {
        bstnode = x;
    }
    else if(x->data < y->data)
    {
        y->lchild = x;
    }
    else
    {
        y->rchild = x;
    }
}
//插入新結點 - 外部函式
void BSTree::insertNode(DataType x)
{
    BSTNode *temp = new BSTNode(x);
    insertNode(root, temp);
}
//列印二叉樹 - 內部函式
void BSTree::printBSTree(BSTNode *bstnode, DataType data, int direction)
{
    if(bstnode != NULL)
    {
        if(direction == 0)
            cout << setw(2) << bstnode->data << " is root " << endl;
        else
            cout << setw(2) << bstnode->data << " is "  << setw(2) << data 
            << "`s"  << setw(12) 
            << (direction == 1?"right child":"left child") << endl;
        printBSTree(bstnode->lchild, bstnode->data, -1);
        printBSTree(bstnode->rchild, bstnode->data, 1);
    }
}

//列印二叉樹 - 外部函式
void BSTree::printBSTree()
{
    if(root == NULL)
        cout << "good job" << endl;
    if(root != NULL)
        printBSTree(root, root->data, 0);
}

//前序遍歷 - 內部函式
void BSTree::preOrder(BSTNode *root)
{
    if(root != NULL)
    {
        cout << root->data << " ";
        preOrder(root->lchild);
        preOrder(root->rchild);
    }
}

//前序遍歷 - 外部函式
void BSTree::preOrder()
{
    preOrder(root);
}

//中序遍歷 - 內部函式
void BSTree::inOrder(BSTNode *root)
{
    if(root != NULL)
    {
        inOrder(root->lchild);
        cout << root->data << " ";
        inOrder(root->rchild);
    }
}

//中序遍歷 - 外部函式
void BSTree::inOrder()
{
    inOrder(root);
}

//後續遍歷 - 內部函式
void BSTree::postOrder(BSTNode *root)
{
    if(root != NULL)
    {
        postOrder(root->lchild);
        postOrder(root->rchild);
        cout << root->data << " ";
    }
}

//後續遍歷 - 外部函式
void BSTree::postOrder()
{
    postOrder(root);
}

//分層遍歷 - 內部函式
void BSTree::levelOrder(BSTNode *root)
{
    //這裡使用佇列儲存二叉樹的每個結點
    //佇列的特性:先進先出
    queue<BSTNode *> q;
    BSTNode *p = root;
    q.push(p);

    while(!q.empty())
    {
        p = q.front();
        cout << p->data << " ";
        q.pop();

        if(p->lchild != NULL)
        {
            q.push(p->lchild);
        }
        if(p->rchild != NULL)
        {
            q.push(p->rchild);
        }
    }
    cout << endl;
}
//分層遍歷 - 外部函式
void BSTree::levelOrder()
{
    levelOrder(root);
}

//查詢鍵值為x的結點 - 內部函式
BSTNode * BSTree::searchNode(BSTNode *root, DataType x)
{
    if(root == NULL || root->data == x)
        return root;
    if(x < root->data)
        return searchNode(root->lchild, x);
    else
        return searchNode(root->rchild, x);
}

//查詢鍵值為x的結點 - 外部函式
BSTNode * BSTree::searchNode(DataType x)
{
    searchNode(root, x);
}

//查詢最小結點 - 內部函式
BSTNode * BSTree::minMem(BSTNode *root)
{
    if(root == NULL)
        return NULL;
    while(root->lchild != NULL)
        root = root->lchild;
    return root;
}
//查詢最小結點 - 內部函式
DataType BSTree::minMem()
{
    BSTNode *p = minMem(root);
    if(p != NULL)
        return p->data;
    return (DataType)NULL;
}

//查詢最大結點 - 內部函式
BSTNode * BSTree::maxMem(BSTNode *root)
{
    if(root == NULL)
        return NULL;
    while(root->rchild != NULL)
        root = root->rchild;
    return root;
}
//查詢最大結點 - 外部函式
DataType BSTree::maxMem()
{
    BSTNode *p = maxMem(root);
    if(p != NULL)
        return p->data;
    return (DataType)NULL;
}
//查詢結點x的後繼結點,即查詢二叉樹中資料值大於該結點的最小值
BSTNode * BSTree::successorNode(BSTNode *x)
{
    //如果x存在右孩子, 則x的後繼結點為“以其右孩子為根的子樹的最小結點
    if(x->rchild != NULL)
        return minMem(x->rchild);
    //如果x沒有右孩子,則x有以下兩種可能:
    //(1)x是”一個左孩子“,則”x的後繼結點“為”它的父結點“
    //(2)x是一個右孩子,則查詢x的最低父結點,並且該父節點要有左孩子,
    //找到這個最低父節點就是x的後繼結點
    BSTNode *y = x->parent;
    while((y != NULL) && (x == y->rchild))
    {
        x = y;
        y = y->parent;
    }
    return y;
}

//查詢結點x的前驅結點,即查詢二叉樹中資料值小於該結點的最大值
BSTNode * BSTree::predecessorode(BSTNode *x)
{
    //如果x存在左孩子,則x的前驅結點就是以其左孩子為根的子樹的最大結點
    if(x->lchild != NULL)
        return maxMem(x->lchild);
    //如果x沒有左孩子,則x有以下兩張可能
    //(1)x是一個右孩子,則x的前驅結點為它的父節點
    //(2)x是一個左孩子,則查詢x的最低父節點,並且該父節點具有右孩子,
    //找到這個最低的父節點就是x的前驅結點
    BSTNode *y = x->parent;
    while((y != NULL) && (x == y->lchild))
    {
        x = y;
        y = y->parent;
    }
    return y;
}

//刪除鍵值為x的結點,並返回該結點 - 內部函式
BSTNode * BSTree::removeNode(BSTNode *root, BSTNode *x)
{
    BSTNode *y = NULL;
    BSTNode *z = NULL;

    if((x->lchild == NULL) || (x->rchild == NULL))
        z = x;
    else
        z = successorNode(x);

    if(z->lchild != NULL)
        y = z->lchild;
    else
        y = z->rchild;

    if(y != NULL)
        y->parent = z->parent;

    if(z->parent == NULL)
        root = y;
    else if(z == z->parent->lchild)
        z->parent->lchild = y;
    else
        z->parent->rchild = y;

    if(z != x)
        x->data = z->data;

    return z;
}
//刪除鍵值為x的結點,並返回該結點 - 外部函式
void BSTree::removeNode(DataType x)
{
    BSTNode *z, *node;
    if((z = searchNode(root, x)) != NULL)
        if((node = removeNode(root, z)) != NULL)
        delete node;
}

main.cpp

#include <iostream>
#include "BSTree.h"

using namespace std;

static int arr[] = {1, 5, 4, 3, 2, 6};
#define SIZE(a) ((sizeof(a))/(sizeof(a[0])))

int main()
{
    int len;
    BSTree *bstree = new BSTree();

    //新增元素
    len = SIZE(arr);
    for(int i = 0; i < len; i++)
    {
        //cout << arr[i] << " ";
        bstree->insertNode(arr[i]);
    }
    bstree->levelOrder();
    cout << bstree->searchNode(4)->parent->data << endl;

    cout << "\n== 前序遍歷: ";
    bstree->preOrder();

    cout << "\n== 中序遍歷: ";
    bstree->inOrder();

    cout << "\n== 後序遍歷: ";
    bstree->postOrder();

    cout << "\n== 層次遍歷: ";
    bstree->levelOrder();
    cout << endl;

    cout << "== 最小值: " << bstree->minMem() << endl;
    cout << "== 最大值: " << bstree->maxMem() << endl;

    cout << "\n== 刪除節點: " <<