数据结构:关于二叉树的一些操作

二叉树
一、二叉树结点的定义
template<class ElemType>
struct BinaryTreeNode
{
    ElemType data;
    BinaryTreeNode* LChild, * RChild;
    BinaryTreeNode() : LChild(NULL), RChild(NULL) {} //构造函数1,用于构造根结点
    BinaryTreeNode(const ElemType& item, BinaryTreeNode* Lptr = NULL, BinaryTreeNode* Rptr = NULL) //构造函数2,用于构造其他结点  
    //函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
    {
        LChild = Lptr;
        RChild = Rptr;
        data = item;
    }
    ElemType getData() { return data; }  //取得结点中的数据
    void SetLChild(BinaryTreeNode* link) { LChild = link; }  //修改结点的左孩子域
    void SetRChild(BinaryTreeNode* link) { RChild = link; }  //修改结点的右孩子域
    void SetData(ElemType value) { data = value; }   //修改结点的data域            
    BinaryTreeNode* GetLChild() const { return LChild; } //获取左孩子结点
    BinaryTreeNode* GetRChild() const { return RChild; } //获取左孩子结点
};

 

但是其实由于结构体成员默认是public,所以其实我们不必定义那么多成员函数,通过->可以直接访问、修改.所以我们定义结构体只要简单的定义:

//二叉树的结点
template<class ElemType>
struct BinaryTreeNode
{
    ElemType data;
    BinaryTreeNode* LChild, * RChild;
    
};

要访问左孩子的数据:只要x->LChild->data、修改右孩子的数据、只要x->RChild->data=....即可.但是我下面的代码都利用的是第一种比较复杂的,就是不破坏ADT抽象类数据类型的封装性,在假设我一开始不知道struct内的数据成员名时,只知道成员函数时进行操作。

 

二、链式二叉树ADT模板类的构建

 

先是模板类的构建,包括一些基本的函数:生成树,有参无参构造函数、返回根结点,返回根节点的元素值,销毁树,销毁子树,判断二叉树是否为空:,当然,拷贝构造函数和重载=号还没写,等有用到再来完善。

//链式二叉树
template<class ElemType>
class BinaryTree {
private:
    BinaryTreeNode<ElemType>* root;   // 头指针
    void BinaryTreeDestroy_Cursive(BinaryTreeNode<ElemType>* T) //销毁树(递归准备,private)
    {
        if (T == NULL)
            return;
        BinaryTreeDestroy_Cursive(T->LChild());
        BinaryTreeDestroy_Cursive(T->RChild());
        delete T;
    }
public:
    //无参数的构造函数
    BinaryTree() :root(NULL) {}
    //带参数的构造函数
    BinaryTree(const ElemType& item)
    {
        root = new BinaryTreeNode<ElemType>(item);
    }
    //生成树
    void makeBinaryTree(const ElemType& item, BinaryTree& left, BinaryTree& right)
    {
        BinaryTreeNode<ElemType>* t = new BinaryTreeNode<ElemType>;
        t->SetData(item);
        t->SetLChild(left);
        t->SetRChild(right);
        root = t;
    }

    //判断二叉树是否为空
    bool BinaryTreeisEmpty()const
    {
        return root == NULL;
    }

    //获取根结点元素值 
    ElemType GetRootData() const
    {
        return root->data;
    }
    //设置根结点 
    void SetRoot(BinaryTreeNode<ElemType>* p)
    {
        root = p;
    }

    //获取根结点 
    BinaryTreeNode<ElemType>* GetRoot() const
    {
        return root;
    }

    //销毁树 
    void BinaryTreeDestroy()
    {
        BinaryTreeDestroy_Cursive(root);
    }

    //销毁子树 
    void ChildDestroy(BinaryTreeNode<ElemType>* r, int flag)//flag=1销毁左子树 flag=0销毁右子树
    {
        if (flag)
        {
            BinaryTreeDestroy_Cursive(r->GetLChild());
        }
        else
        {
            BinaryTreeDestroy_Cursive(r->GetRChild());
        }
    }

    //拷贝构造函数
   // BinaryTree(BinaryTree& b)

   //析构函数
   // ~BinaryTree() 
   // { BinaryTreeDestroy(); }

   //重载函数:赋值
   //LinkList& operator=(LinkList&List);
}

 

三、成员函数:location 函数

传入一个元素,一个引用的空结点指针p,和一个带查找的头结点指针r,利用递归来查找是否有结点元素为x,如果有,就返回true,并且让p指向这个结点,否则返回false,并且p为NULL。前提是前提是只有一个结点为x。

  bool Location(ElemType& x, BinaryTreeNode<ElemType>* r, BinaryTreeNode<ElemType>*& p)
    {
        if (!r)
        {
            p = NULL;
            return false;
        }
        else if (r->getData() == x)
        {
            p = r;
            return true;
        }
        else if (Location(x, r->GetLChild(), p) || Location(x, r->GetRChild(), p))
        {
            return true;
        }
        else
            return false;
    }

 

四、成员函数:递归的前序后序中序遍历二叉树

 //前序遍历 
    void PreOrderTraverse(BinaryTreeNode<ElemType>* T) const
    {
        if (T)
        {
            cout << T->getData();
            PreOrderTraverse(T->GetLChild());
            PreOrderTraverse(T->GetRChild());
        }
    }
    //中序遍历 
    void InOrderTraverse(BinaryTreeNode<ElemType>* T) const
    {
        if (T)
        {
            InOrderTraverse(T->GetLChild());
            cout << T->getData();
            InOrderTraverse(T->GetRChild());
        }
    }
    //后序遍历 
    void PostOrderTraverse(BinaryTreeNode<ElemType>* T) const
    {
        if (T)
        {
            PostOrderTraverse(T->GetLChild());
            PostOrderTraverse(T->GetRChild());
            cout << T->getData();
        }
    }

 

五、成员函数:递归的前序遍历二叉树的构建

传入一个数组,和空指针的符号empty :就是数组中的empty就表示空指针,数组是前序遍历二叉树的序列,来构建二叉树,再传入一个参数n代表递归到了第几个数字,方便递归

 //建立二叉树的存储结构
    BinaryTreeNode<ElemType>* CreateBinaryTree(vector<ElemType>& x, ElemType& empty, int& n)
    {
        ElemType ch = x[n];
        n++;
        if (ch == empty)
        {
            return NULL;
        }
        else
        {
            BinaryTreeNode<ElemType>* Node = new BinaryTreeNode<ElemType>;
            Node->data = ch;
            Node->LChild = CreateBinaryTree(x, empty, n);
            Node->RChild = CreateBinaryTree(x, empty, n);
            return Node;
        }
    }

 

六、外部函数:递归计算二叉树结点数

传入一个树的根

//计算二叉树的节点数
template<class ElemType>
int BinaryTreeSize(BinaryTreeNode<ElemType>* T)
{
    if (T == NULL)
        return 0;
    else
        return (BinaryTreeSize(T->GetLChild()) + BinaryTreeSize(T->GetRChild()) + 1);
}

 

七、外部函数:替换左右子树

传入一个二叉树的根 也可以是子树的根

//替换左右子树
template<class ElemType>
void BinaryTree_Revolute_Cursive(BinaryTreeNode<ElemType>* root)
{
    if (root->GetLChild())
    {
        BinaryTree_Revolute_Cursive(root->GetLChild());
    }
    if (root->GetRChild())
    {
        BinaryTree_Revolute_Cursive(root->GetRChild());
    }
    BinaryTreeNode<ElemType>* temp = root->GetLChild();
    root->SetLChild(root->GetRChild());
    root->SetRChild(temp);
    return;
}

 

八、外部函数:求二叉树的深度

传入一个二叉树的根

//求二叉树的层数
template<class ElemType>
int Cal_BinaryTree_Layers(BinaryTreeNode<ElemType>* T)
{
    if (!T)
        return 0;
    else
    {

        return max(Cal_BinaryTree_Layers(T->GetLChild()), Cal_BinaryTree_Layers(T->GetRChild())) + 1;
    }
}

 

八、外部函数:求以结点x为根的深度

//求结点x为头结点的深度
template<class ElemType>
int GetBinaryTreeHeight(BinaryTree<ElemType>& T, ElemType& x)
{
    BinaryTreeNode<ElemType>* y = NULL;
    T.Location(x, T.GetRoot(), y);
    if (!y)
        return 0;
    return  Cal_BinaryTree_Layers(y);
}

 

九、外部函数:以结点x的双亲 

有两个重载:结点x为指针或者给的是data

 

重载1:

template<class ElemType>//重载1 利用data寻找双亲结点 
BinaryTreeNode<ElemType>* getparent(BinaryTreeNode<ElemType>* root, ElemType& x)
{
    if (root->GetLChild() && root->GetLChild()->getData() == x)
    {
        return root;
    }
    if (root->GetRChild() && root->GetRChild()->getData() == x)
    {
        return root;
    }
    if (root->GetLChild())
    {
        return getparent(root->GetLChild(), x);
    }
    if (root->GetRChild())
    {
        return getparent(root->GetRChild(), x);
    }
    if (!root->GetLChild() && !root->GetRChild())
    {
        return NULL;
    }
}

 

重载2:

 

template<class ElemType>//重载2 利用指针寻找双亲结点
BinaryTreeNode<ElemType>* getparent(BinaryTreeNode<ElemType>* root, BinaryTreeNode<ElemType>* p)
{
    if (root->GetLChild() && root->GetLChild() == p)
    {
        return root;
    }
    if (root->GetRChild() && root->GetRChild() == p)
    {
        return root;
    }
    if (root->GetLChild())
    {
        return getparent(root->GetLChild(), p);
    }
    if (root->GetRChild())
    {
        return getparent(root->GetRChild(), p);
    }
    if (!root->GetLChild() && !root->GetRChild())
    {
        return NULL;
    }
}

 

 

十、外部函数:求结点x的兄弟结点

flag :0代表求左兄弟 1代表求右兄弟

//求x的兄弟结点 flag :0代表求左兄弟 1代表求右兄弟
template<class ElemType>
BinaryTreeNode<ElemType>* GetSibling(BinaryTree<ElemType>& T, ElemType& x, int flag)
{
    BinaryTreeNode<ElemType>* temp;
    if (!T.Location(x, T.GetRoot(), temp))
        return NULL;
    BinaryTreeNode<ElemType>* ans = getparent(T.GetRoot(), x);
    if (!ans)
        return NULL;
    if (!flag)
    {
        if (x == ans->GetLChild()->getData())
        {
            return NULL;
        }
        else
        {
            return ans->GetLChild();
        }
    }
    if (flag)
    {
        if (x == ans->GetRChild()->getData())
        {
            return NULL;
        }
        else
        {
            return ans->GetRChild();
        }
    }
}

 

十一、外部函数:删除以x为根节点的孩子

flag :0代表求左兄弟 1代表求右兄弟

template<class ElemType>
bool DeleteChild(BinaryTree<ElemType>& T, ElemType& x, int flag)
{
    BinaryTreeNode<ElemType>* temp;
    T.Location(x, T.GetRoot(), temp);
    if (!temp)
        return false;
    if (!flag)
    {
        if (temp->GetLChild())//子树本来为空也算删除失败
            temp->SetLChild(NULL);
        else
            return 0;
    }
    else
    {
        if (temp->GetRChild())
            temp->SetRChild(NULL);
        else
            return 0;
    }
    return true;
}

 

十二、外部函数:求叶子结点数

//求叶子结点数
template<class ElemType>
int count0(BinaryTreeNode<ElemType>* r)
{
    if (!r->GetLChild() && !r->GetRChild())
        return 1;
    if (r->GetLChild() && r->GetRChild())
        return count0(r->GetLChild()) + count0(r->GetRChild());
    else if (r->GetLChild())
        return count0(r->GetLChild());
    else if (r->GetRChild())
        return count0(r->GetRChild());
}

template<class ElemType>
int LeafCount(BinaryTree<ElemType>& T)//求叶子结点数
{
    return count(T.GetRoot());
}

 

 

 

十三、外部函数:求叶子结点数

//度为2的节点数
template<class ElemType>
int count2(BinaryTreeNode<ElemType>* r)
{
    if (!r->GetLChild() && !r->GetRChild())
        return 0;
    if (r->GetLChild() && r->GetRChild())
        return count2(r->GetLChild()) + count2(r->GetRChild()) + 1;
    else if (r->GetLChild())
        return count2(r->GetLChild());
    else if (r->GetRChild())
        return count2(r->GetRChild());
}
//度为2的节点数
template<class ElemType>
int CountDegreeTwo(BinaryTree<ElemType>& T)
{
    return count2(T.GetRoot());
}

 

十四、外部函数:输出根到结点x的路径

可能有多个x结点

vector<BinaryTreeNode<string>*>q;//路径数组
//输出某一个顶点到某一个结点的路径
template<class ElemType>
void dfs_path(BinaryTreeNode<ElemType>* p, ElemType s)
{
    q.push_back(p);
    if (p->getData() == s)
    {
        int i = 0;
        for (i = 0; i < q.size() - 1; ++i)
            cout << q[i]->getData() << "->";
        cout << q[i]->getData() << endl;
    }
    if (p->GetLChild())
    {
        dfs_path(p->GetLChild(), s);
    }
    if (p->GetRChild())
    {
        dfs_path(p->GetRChild(), s);
    }
    q.pop_back();//回溯出队
}
template<class ElemType>
void finds_path(BinaryTree<ElemType>& T, ElemType s)
{
    dfs_path(T.GetRoot(), s);
}

 

十五、外部函数:非递归的先序中序后序遍历

//非递归的先序遍历输出
template<class ElemType>
void preorder(BinaryTree<ElemType>& T)
{
    stack<BinaryTreeNode<ElemType>*>s;
    BinaryTreeNode<ElemType>* root = T.GetRoot();
    while (root || !s.empty())
    {
        //go left down to the ground
        while (root)
        {
            pre.push_back(root->getData());
            s.push(root);
            root = root->GetLChild();
        }
        root = s.top()->GetRChild();
        s.pop();
    }
}

//非递归的中序遍历输出
template<class ElemType>
void inorder(BinaryTree<ElemType>& T)
{
    stack<BinaryTreeNode<ElemType>*>s;
    BinaryTreeNode<ElemType>* root = T.GetRoot();
    while (root || !s.empty())
    {
        while (root)
        {
            s.push(root);
            root = root->GetLChild();
        }
        in.push_back(s.top()->getData());
        root = s.top()->GetRChild();
        s.pop();
    }
}

//非递归的后序遍历输出
template<class ElemType>
void postorder(BinaryTree<ElemType>& T)
{
    stack<BinaryTreeNode<ElemType>*>s;
    BinaryTreeNode<ElemType>* root = T.GetRoot();
    while (root || !s.empty())
    {
        while (root)
        {
            post.push_back(root->getData());
            s.push(root);
            root = root->GetRChild();
        }
        root = s.top()->GetLChild();
        s.pop();
    }
    reverse(post.begin(), post.end());
}

 

十六、外部函数:层次遍历构建二叉树

传入一个层次遍历的数组,构建二叉树,利用队列:

template<class ElemType>
void CreateTree_Layer(BinaryTree<ElemType>& T, ElemType& str, ElemType& empty)//层次结构建立二叉树
{
    vector<string>s = depart_string(str);
    queue<BinaryTreeNode<ElemType>*>q;
    int b = 0;
    BinaryTreeNode<ElemType>* temp = new BinaryTreeNode<ElemType>;;
    createnode(temp, s[b]);
    q.push(temp);
    T.SetRoot(temp);
    while (!q.empty())
    {
        BinaryTreeNode<ElemType>* x = q.front();
        q.pop();
        if (b == s.size() - 1)//关键点 ①:他最后的空指针有可能缺省
        {
            x->SetLChild(NULL);
            x->SetRChild(NULL);
            continue;
        }
        if (s[++b] != empty)
        {
            BinaryTreeNode<ElemType>* t = new BinaryTreeNode<ElemType>;;
            createnode(t, s[b]);
            x->SetLChild(t);
            q.push(t);
        }
        else
        {
            x->SetLChild(NULL);
        }
        if (b == s.size() - 1)//关键点 ②:他最后的空指针有可能缺省 可能只写了最后一个结点的左边空指针,没有写右边空指针
        {
            x->SetRChild(NULL);
            continue;
        }
        if (s[++b] != empty)
        {
            BinaryTreeNode<ElemType>* t = new BinaryTreeNode<ElemType>;;
            createnode(t, s[b]);
            x->SetRChild(t);
            q.push(t);
        }
        else
        {
            x->SetRChild(NULL);
        }
    }


}

 

十七、求两个结点A、B最近的祖先

利用到了前面求路径函数,我们加以改造,加入一个引用型vector数组ans,在到达目的地结点时,将整个q数组copy到ans里,这样ans里面就是根到A的路径,再求出根到B的路径。 然后我们先pop_back一下两个ans数组,因为是求共同的祖先,如果肯定不能算自己。然后把两个数组利用algorithm的函数reverse一下,这样可以保证离结点最近的祖先在数组的前面,先遍历到,因为是求最近的祖先。然后用二重循环遍历两个数组先出现的相同的元素,就是对于比较短的路径的每一个祖先,在长的路径中寻找看是否出现过,出现过就是最近的。对于数组的长短,我们可以if判断一下,默认a短b长,如果a长,就swap一下。

求路径数组:

/把根节点到结点的路径存储在ans中
template<class ElemType>
void dfs_path(BinaryTreeNode<ElemType>* p, ElemType s, vector<BinaryTreeNode<string>*>& ans)
{
    q.push_back(p);
    if (p->getData() == s)
    {
        for (int i = 0; i < q.size(); ++i)
            ans.push_back(q[i]);
        return;
    }
    if (p->GetLChild())
    {
        dfs_path(p->GetLChild(), s,ans);
    }
    if (p->GetRChild())
    {
        dfs_path(p->GetRChild(), s,ans);
    }
    q.pop_back();//回溯出队
}

template<class ElemType>
vector<BinaryTreeNode<string>*>find_path_vec(ElemType x,BinaryTree<ElemType>&T)
{
    q.clear();
    vector<BinaryTreeNode<string>*>ans;
    dfs_path(T.GetRoot(),x,ans);
    return ans;
}

 

求最近共同祖先:

//寻找两个结点最近的共同祖先
template<class ElemType>
ElemType findtwosame(BinaryTree<ElemType>& T, ElemType a, ElemType b)
{
    vector<BinaryTreeNode<string>*>ans1 = find_path_vec(a,T);
    vector<BinaryTreeNode<string>*>ans2 = find_path_vec(b,T);
    ans1.pop_back();//关键点1 不能算自己
    ans2.pop_back();
    reverse(ans1.begin(), ans1.end());//关键点2 reverse一下便于寻找祖先
    reverse(ans2.begin(), ans2.end());
    ElemType ans;
    int i = 0;
    int l1 = ans1.size();
    int l2 = ans2.size();
    if (l1 > l2)
    {
        vector<BinaryTreeNode<string>*>temp = ans1;
        ans1 = ans2;
        ans2 = temp;
    }
    for (int i = 0; i < l1; ++i)
    {
        for (int j = 0; j < l2; ++j)
        {
            if (ans1[i]->getData()== ans2[j]->getData())
            {
                ans = ans1[i]->getData();
                return ans;
            }
        }
    }
}

 

 

目前只运用到这些代码,有其他新的,我会加以补充。

posted @ 2022-04-24 15:03  朱朱成  阅读(68)  评论(0编辑  收藏  举报