Definition

K+R

  • (tree ),有且仅有一个根结点
  • (root )
  • 路径(path )
  • 父结点子结点
  • (edge ), k,k
  • 兄弟结点
  • 树叶(leaf )
  • 结点的度(degree )
  • 祖先(ancestor )
  • 子孙
  • 层数(level ),根结点的层数为 0
  • 有序树(ordered tree )
  • 二叉树(binary tree ),度为2且严格区分左右结点的有序树
  • 森林(forest )

森林与二叉树的等价转换

森林到二叉树

左子右兄原则

  1. 将兄弟结点都连起来
  2. 将父结点和除了第一个子结点的其他子结点之间的连线断开

二叉树到森林

  1. 断开所有父结点和右子结点
  2. 连接所有父结点和左子结点的右子结点以及右子结点的右子结点…

抽象数据类型

结点

template<class T>
class TreeNode{
public:
    TreeNode(const <T>& value);
    virtual ~TreeNode();

    T Value();
    TreeNode*<T> LeftMostChild();
    TreeNode*<T> RightSibling();

    bool IsLeaf();
    void SetValue(T value);
    void SetChild(TreeNode<T>* leftchild);
    void SetSibling(TreeNode<T>* rightsibling);
};

template <T>
class Tree{
    Tree();
    virtual ~Tree();

    bool isEmpty();
    TreeNode<T>* GetRoot(TreeNode<T>* rootValue);
    TreeNode<T>* Parent(TreeNode<T>* current);
    TreeNode<T>* PreSibling(TreeNode<T>* current);
    TreeNode
    void CreateRoot(const T& rootValue);
    void RootFirstTraverse(TreeNode<T>* root);
    void RootLastTraverse(TreeNode<T>* root);
    void WidthTraverse(TreeNode<T>* root);
    void DelSubTree(TreeNode<T>* subroot);
};

周游

  • 先根、后根
  • 先根周游和该树对应的二叉树的中序周游等价

先根周游代码

//for循环
template<T>
void Tree<T>::RootFirstTraverse(TreeNode<T>* root)
{
    if(root == NULL)
        return;
    Visit(root);
    for(TreeNode<T>* n = root->LeftMostChild();n != NULL;n = root->RightSibling())
        RootFirstTraverse(n);
}
//while循环
template<T>
void Tree<T>::RootFirstTraverse(TreeNode<T>* root)
{
    while(root != NULL)
    {
        Visit(root);
        RootFirstTraverse(root->LeftMostChild());
        root = root->RightSibling()
    }
}

后根周游代码

//for循环
template<T>
void Tree<T>::RootLastTraverse(TreeNode<T>* root)
{
    if(root == NULL)
        return;
    for(TreeNode<T>* n = root.LeftMostChild();n != NULL;n = root.RightSibling())
        RootLastTraverse(n);
    Visit(root);
}
//while循环
template<T>
void Tree<T>::RootFirstTraverse(TreeNode<T>* root)
{
    while(root != NULL)
    {
        RootFirstTraverse(root->LeftMostChild());
        Visit(root);
        root = root->RightSibling();
    }
}

广度优先周游

template<T>
void Tree<T>::WidthTraverse(TreeNode<T>* root)
{
    using std::queue;
    queue<TreeNode<T>*> Q;
    while(root != NULL)  //把森林的根结点加入队列
    {
        Q.push(root);
        root = root->RightSibling();
    }
    while(!Q.empty())  //广搜
    {
        TreeNode<T>* current = Q.front();
        Q.pop();
        Visit(n);
        TreeNode<T>* next = current->LeftMostChild();
        while(next != NULL)
        {
            Q.push(next);
            next = next->RightSibling();
        } 
    }
}

链式存储结构

“子结点表”表示法

  • 结点有三个域:值+指向父结点的指针域+指向子结点表的指针域
  • 子结点表是一个链表,其中每个结点有两个域:当前的代表的树结点的指针域+指向下一个子结点的指针域
    子结点表法

静态“左子右兄”表示法

  • 每个结点有4个域:值+指向父结点的指针域+指向第一个子结点的指针域+指向右侧兄弟结点的指针域
  • 空间效率更高,且每个结点的存储空间固定

静态左子右兄表示法

动态表示法

  • 为每个结点分配可变的存储空间
  • 每个结点有不确定个域:值+子结点个数n+指向n个子结点的指针域

动态“左子右兄”二叉链表表示法

代码

template<class T>
class TreeNode{
private:    //增加私有数据成员
    T m_Value;
    TreeNode<T>* pChild;
    TreeNode<T>* pSibling;
public:
    TreeNode(const <T>& value);
    virtual ~TreeNode();

    T Value();
    TreeNode*<T> LeftMostChild();
    TreeNode*<T> RightSibling();

    bool IsLeaf();
    void SetValue(T value);
    void SetChild(TreeNode<T>* leftchild);
    void SetSibling(TreeNode<T>* rightsibling);
}

template<T>
bool TreeNode<T>::IsLeaf()
{
    if(pChild == NULL)
        return true;
    ruturn 
        false;
}

template<T>
void TreeNode<T>::SetValue(const T& value)
{
    m_Value = value;
}

template<T>
void TreeNode<T>::SetChild(TreeNode<T>* leftchild)
{
    pChild = leftchild;
}

父指针表示法

父指针表示法(parent pointer)

  • 结点用数组存储,每个结点有两个域,值+指向父结点的指针域
  • 寻找父结点的时间复杂度是O(1)

并查集

  • 并查集,不相交的子集构成的特殊集合
  • 基本操作:Find, Union
  • 一种基本数据类型,用于求解等价类问题

划分等价类的过程

  1. 初始情况,每个元素都是一个独立等价类
  2. 每次读入关系集 R 中的一个偶对x,y,判断 x,y 所属的等价类是否相等。如果不相等,那么把其中一个等价类并到另一个里面,再把这个等价类置空。
  3. 完成关系对的读入之后,剩下的所有非空子集就是所有的等价类

用父指针表示法实现划分等价类

  • 一个森林中的子树代表初始时的独立等价类
  • 判断 x,y 是否在同一个等价类中,分别找到两个结点所在的树的根,根不相同则不在同一个树中
  • 把一个树的根接到另一个树的根上就实现了合并的操作

重量权衡合并规则

  • 上述父指针表示法实现划分等价类的最坏情况是最后成了一个单链,树的深度 O(n)
  • 为了防止这种情况,用“重量权衡合并规则”加以改进
  • 合并之前先判断两个树的结点个数,让个数少的树根接到个数多的树根上。树的深度 O(logn) ,Find操作需要 O(logn) 时间

代码实现

template<T>
class ParTreeNode{
private:
    T value;
    ParTreeNode<T>* parent;
    int nCount;
public:
    ParTreeNode();
    virtual ~ParTreeNode(){};

    T getValue();
    ParTreeNode<T>* getParent();
    int getCount();
    void setValue(const T& val);
    void setParent(ParTreeNode<T>* par)
    void setCount(const int con);
};

template<T>
ParTreeNode<T>::ParTreeNode()
{
    parent = NULL;
    nCount = 1;
}

template<T>
T ParTreeNode<T>::getValue()
{
    return value;
}

template<T>
ParTreeNode<T>* ParTreeNode<T>::getParent()
{
    return parent;
}

template<T>
int ParTreeNode<T>::getCount()
{
    return nCount;
}

template<T>
void ParTreeNode<T>::setValue(const T& val)
{
    value = val;
}

template<T>
void ParTreeNode<T>::setParent(ParTreeNode<T>* par)
{
    parent = par;
}

template<T>
void ParTreeNode<T>::setCount(const int count)
{
    nCount = count;
}
template<T>
class ParTree{
public:
    ParTreeNode<T>* array;
    int Size;

    ParTree<T> (const int size);
    virtual ~ParTree<T> ();

    ParTreeNode<T>* Find(ParTreeNode<T>* node);
    void Union(int i, int j);
    bool Different(int i,int j);
};

template<T>
ParTree<T>::ParTree(const int size)
{
    array = new ParTreeNode[size];
    Size = size;
}

template<T>
ParTree<T>::~ParTree()
{
    delete array;
}

template<T>
ParTreeNode ParTree<T>::Find(ParTreeNode<T>* node)
{
    ParTreeNode<T>* pointer = node;
    while(pointer->parent != NULL)
    {
        pointer = pointer->parent;
    }
    return pointer;
}

template<T>
bool ParTree<T>::Different(int i,int j)
{
    return Find(&array[i]) = ParTree::Find(&array[j]);
}

template<T>
void ParTree<T>::Union(int i,int j)
{
    ParTreeNode<T>* pointeri = ParTree::Find(&array[i]);
    ParTreeNode<T>* pointerj = ParTree::Find(&array[j]);
    if(pointeri != pointerj)
    {
        if(pointeri->getCount() <= pointerj->getCount())
        {
            pointeri->setParent(pointerj);
            pointerj->setCount(pointeri->getCount()+pointerj->getCount());
        }
        else
        {
            pointerj->setParent(pointeri);
            pointeri->setCount(pointeri->getCount()+pointerj->getCount());
        }
    }
}

路径压缩算法

  • 在实现Find函数的同时将路径进行压缩。不仅返回根结点,还把该结点的所有祖先结点都变成根结点的子结点。
template<T>
ParTreeNode<T>* ParTree<T>::FindPC(ParTreeNode<T>* node)
{
    if(node->getParent() == NULL)
        return node;
    node->setParent(FindPC(node->getParent()));  //递归
    return node->getParent();
}

Thanks to Wang Tengjiao

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值