树结构

作者: Re丶Allen | 来源:发表于2018-03-16 20:39 被阅读0次

树:层次关系
Tree :n个节点构成的有限集合;
n=0时;称为空树;
对于非空树,具备特质有:

  • 树中有一个根的特殊节点,用r解释;
  • 子树;
    树与非树?
  • 子树是不想交的;
  • 除了根节点外,每个结点点有且仅有一个父节点;
  • 一棵N个结点的树有N-1条边;

基本术语

1、 结点的度 结点的子树个数
2、树的度:树的所有结点最大的度数
3、叶结点:度为1的结点;
4、父节点:有子树的结点是其子树的根节点的父节点
5、子节点;
6、兄弟结点:同一父节点的各结点是彼此的兄弟结点;
7、路径和路径长度;
8、祖先结点;
9、子孙节点;
11、结点的层次;
12、树的深度;

实现方法


image.png

二叉树:度为2;
特殊二叉树

  • 斜二叉树
  • 完美二叉树/满二叉树
  • 完全二叉树(不完整)

重要性质

二叉树第i层最大结点 2^(n-1)
深度为k的二叉树最大结点总数为2^n-1
非空二叉树 n0为叶节点的个数 n2为度为2的非叶节点个数 满足n0=n2+1;

常用的遍历方法:
先序--根,左子树,右子树
中序--左子树,根,右子树
后续--左子树,右子树,根
层次遍历--从上到下,从左到右

二叉树的存储结构

顺序存储结构

-完全二叉树
:从上到下、从左到右顺序存储n个节点的完全二叉树的节点父子关系。

  • 父根节点 父节点[i/2]
  • 左孩子节点 2i
  • 右孩子节点2i+1
    一般二叉树也可以采取这种结构,但会造成空间浪费。

链表存储

template <typename DataType>
struct TreeNode
{
    DataType data; //存储的数据
    TreeNode *left; //指向左子树根节点
    TreeNode *right; //指向右子树根节点
    //TreeNode * parent; //指向父节点,如果需要的话
    TreeNode(DataType inData): data(inData), right(nullptr), left(nullptr) {}
};
image.png

遍历

//遍历
//先序遍历
template <typename DataType>
void Traverse(TreeNode<DataType> * inNode){
    if (inNode == nullptr){
    return;
    }
    //cout<<inNode->data<<endl;//如果在这里访问即为先序访问
    Traverse(inNode->left);
    //cout<<inNode->data<<endl;//如果在这里访问即为中序访问
    Traverse(inNode->right);
    //cout<<inNode->data<<endl;//如果在这里访问即为后序访问
    return;
}

二叉树的非递归算法

先序遍历的非递归:使用堆栈

template <typename DataType>
void NonRecursiveTraverse(TreeNode<DataType> * inNode){
    stack<TreeNode<DataType>*> nodeStack;
    TreeNode<DataType> *cycleNode = inNode;
    while(inNode != nullptr||!nodeStack.empty()){
        //不断访问某一节点的左子节点并压栈知道最左子节点
        while(cycleNode != nullptr){
            //遇到节点先输出
            cout<<cycleNode->data<<endl;
            nodeStack.push(cycleNode);
            cycleNode = cycleNode->left;
        }
        //此时所有的左子树都压入栈中
        //弹出最后一个节点 访问该节点的右子节点并作为下一步作为下一轮遍历节点
        if(!inNode.empty()){
            cycleNode = nodeStack.top();
            cycleNode = cycleNode->right;
            nodeStack.pop();
        }
    }
}

中序遍历:先访问左子节点,在访问根节点,再访问右子节点。

template <typename DataType>
void NonRecursiveTraverse(TreeNode<DataType> * inNode){
    stack<TreeNode<DataType>*> nodeStack;
    TreeNode<DataType> *cycleNode = inNode;
    while(inNode != nullptr||!nodeStack.empty()){
        //不断访问某一节点的左子节点并压栈知道最左子节点
        while(cycleNode != nullptr){   
            nodeStack.push(cycleNode);
            cycleNode = cycleNode->left;
        }
        //此时所有的左子树都压入栈中
        //弹出最后一个节点 访问该节点的右子节点并作为下一步作为下一轮遍历节点
        if(!inNode.empty()){
            cycleNode = nodeStack.top();
            //在此处访问即为中序遍历,时机为压入右子节点之前
        //cout << cycleNode->data << endl; 
            cycleNode = cycleNode->right;
            nodeStack.pop();
        }
    }
}

由于辅助压栈,我们并没有将null压入栈中,如果发现左子节点为null则在保存右子节点地址后直接弹出该节点,并使用右子节点作为下一论访问起始节点,如果右子节点为null则表示该节点左右子树均遍历完毕,则继续弹出直至出现第一个右子树不为空的节点,重复递归。

压栈图中,在前序遍历时,只要遇到节点(压栈过程)就直接输出就可以保证根节点首先被输出,而中序遍历由于需要在左子树输出完毕后才能输出,因此只要保证在压栈返回时(出栈时)且准备遍历右子树时输出即可。

后序遍历 非递归实现

template <typename DataType>
void NonRecursiveTraverse(TreeNode<DataType> *inNode){
        stack<TreeNode<DataType> *>nodeStack;
        TreeNode<DataType> *cycleNode = inNode;
        TreeNode<DataType> *hasNode = nullptr;
        while(inNode != nullptr || !nodeStack.empty()){
            //不断访问某一节点的左子节点并压栈直到最左子节点
            while(cycleNode != nullptr){
                nodeStack.push(cycleNode);
                cycleNode = cycleNode->left;
            }
            //此时所有的子节点都已经压入栈中。
            //弹出最后一个节点,访问该节点的右子节点并作为下一轮遍历根节点
            if(!nodeStack.empty()){
                cycleNode = nodeStack.top();
                //此时判别是否已经加载右子树
                //如果为空则和中序遍历一样
                if(cycleNode->right == nullptr){
                    hascycle = cycleNode;
                    cout<< cycleNode->data<<endl;
                    nodeStack.pop();
                }
                else if(hascycle == cycleNOde->right){
                    hascycle = cycleNode;
                    cout<<cycleNode->data<<endl;
                    nodeStack.pop();
                }
                cycleNode = nullptr;
                if(!nodeStack.empty() && ndoeStack.top->right!=nullptr)) {
                    cycleNode = nodeStack.top()->right;
                }
            }
        }
}

层序遍历

二叉树遍历的核心问题:二维结构的线性化

  • 从节点访问其左、右儿子
  • 需要一个存储结构保存暂时不访问的结点
  • 存储结构:堆栈、队列

队列实现:遍历从根节点开始,首先将根节点入队,然后开始执行循环:结点出队,访问该结点,将左右儿子入队

void LevelOrderTraversal ( BinTree BT){
    Queue Q;BinTree T;
    //如果是空树直接返回
    if(!BT)
        return ;
    //创建并初始化队列Q
    Q = CreatQueue(Maxsize);
    AddQ(Q,BT);
    while( !IsEmptyQ(Q)){
        T = DeleteQ(Q);
        cout  <<T->data<<endl;
        if(T->left)   
            AddQ(Q,T->left);
        if(T->right)
            AddQ(Q,T->right)
    }
}

如果有两个遍历序列确定二叉树

必须要有中序遍历
如果已知先序和中序;

  1. 根据先序遍历序列第一个节点确定根节点;
  2. 根绝根节点在中序遍历序列中分割出左右两个子序列
  3. 对左子树和右子树分别递归分为左子树和右子树

二叉查找树的定义与实现

静态查找:二分查找
动态查找:二叉搜索树;
也称为二叉排序树,或者二叉查找树;

  • 分空左子树的所有键值小于其根节点的键值。
  • 分空右子树的所有键值大于其根节点的键值。
  • 左右子树都是二叉搜索树。

对于二叉树ADT,一般需要提供以下操作:

  • 清空二叉查找树:MakeEmpty
  • 查找某个节点:Find
  • 删除某个节点:DeleteElement
  • 查找最大值:FindMax
  • 查找最小值:FindMin
  • 插入一个节点:InsertElement
    二叉查找树的平均深度为O(log(N)),不过如果插入元素序列递增或者递减,二叉查找树将退化成单链表。

二叉查找树的实现

二叉树的基本结构定义:

template <typename DataType>
struct Node
{
    DataType data;
    Node *left;
    Node *right;
    Node(DataType inData): data(inData), left(nullptr), right(nullptr) {}
};

template <typename DataType>
class BinarySearchTree
{
public:
    BinarySearchTree(): root(nullptr) {}
    ~BinarySearchTree();
    void MakeEmpty(); //清空二叉查找树
    void MakeEmptyNon(); //非递归清空二叉树
    Node<DataType> * Find(DataType inElement); //查找某个元素
    Node<DataType> * FindNon(DataType inElement); //非递归查找
    void DeleteElement(DataType inElement); //删除一个节点
    Node<DataType> * FindMax(); //查找最大元素
    Node<DataType> * FindMaxNon(); //非递归查找最大元素
    Node<DataType> * FindMin(); //查找最小元素
    Node<DataType> * FindMinNon(); //非递归查找最小元素
    Node<DataType> * InsertElementNon(DataType inElement); //非递归插入一个元素
private:
    void MakeEmptyCore(Node<DataType> *inNode);
    Node<DataType> *FindCore(Node<DataType> *inNode, DataType inElement);
    //删除一个节点
    Node<DataType> * DeleteElementCore(Node<DataType> *inNode, DataType inElement);
    Node<DataType> *FindMaxCore(Node<DataType> *inNode);
    Node<DataType> *FindMinCore(Node<DataType> *inNode);
    Node<DataType> *root;
};

二叉查找树的基本数据成员为
递归清空核心函数:

template <typename DataType>
void BinarySearchTree<DataType>::MakeEmptyCore(Node<DataType> *inNode)
{
    if (inNode == nullptr) {
        return;
    }
    MakeEmptyCore(inNode->left);
    MakeEmptyCore(inNode->right);
    delete inNode;
}

递归清空核心函数

template <typename DataType>
void BinarySearchTree<DataType>::MakeEmptyCore(Node<DataType> *inNode)
{
    if (inNode == nullptr) {
        return;
    }
    MakeEmptyCore(inNode->left);
    MakeEmptyCore(inNode->right);
    delete inNode;
}

递归清空入口函数,调用清空核心函数

template <typename DataType>
void BinarySearchTree<DataType>::MakeEmpty()
{
    MakeEmptyCore(root); root = nullptr;
}

非递归清空函数,采用某种非递归遍历函数思想即可

template <typename DataType>
void BinarySearchTree<DataType>::MakeEmptyNon()
{
    stack<Node<DataType> *> nodeStack;
    Node<DataType> *cycleIter = root;
    while (cycleIter != nullptr || !nodeStack.empty()) {
        while (cycleIter != nullptr) {
            nodeStack.push(cycleIter);
            cycleIter = cycleIter->left;
        }
        
        if (!nodeStack.empty()) {
            Node<DataType> * tmp = nodeStack.top();
            cycleIter = tmp->right;
            delete tmp; nodeStack.pop();
        }
    }
    root = nullptr;
}

递归查找某个元素

template <typename DataType>
Node<DataType> *BinarySearchTree<DataType>::FindCore(Node<DataType> *inNode, DataType inElement)
{
    if (inNode == nullptr) {
        return nullptr;
    }
    if (inNode->data == inElement) {
        return inNode;
    }
    else if (inNode->data > inElement){
        return FindCore(inNode->left, inElement);
    }
    else {
        return FindCore(inNode->right, inElement);
    }
    return nullptr;
}

非递归查找

template <typename DataType>
Node<DataType> * BinarySearchTree<DataType>::FindNon(DataType inElement)
{
    Node<DataType> *cycleIter = root;
    while (cycleIter != nullptr) {
        if (cycleIter->data == inElement) {
            return cycleIter;
        }
        else if (cycleIter->data > inElement){
            cycleIter = cycleIter->left;
        }
        else {
            cycleIter = cycleIter->right;
        }
    }
    return nullptr;
}

递归删除节点函数核心函数

template <typename DataType>
Node<DataType> * BinarySearchTree<DataType>::DeleteElementCore(Node<DataType> *inNode, DataType inElement)
{
    if (inNode == nullptr) {
        return nullptr;
    }
    else if (inNode->data > inElement) {
        inNode->left = DeleteElementCore(inNode->left, inElement);
    }
    else if (inNode->data < inElement) {
        inNode->right = DeleteElementCore(inNode->right, inElement);
    }
    else {
        if (inNode->left != nullptr && inNode->right != nullptr) {
            Node<DataType> *tmp = FindMinCore(inNode->right);
            inNode->data = tmp->data;
            inNode->right = DeleteElementCore(inNode->right, inNode->data);
        }
        else {
            Node<DataType> *tmp = inNode;
            if (inNode->left == nullptr) {
                inNode = inNode->right;
            }
            else {
                inNode = inNode->left;
            }
            delete  tmp;
        }
    }
    return inNode;
}

递归查找最大元素核心函数

template <typename DataType>
Node<DataType> * BinarySearchTree<DataType>::FindMaxCore(Node<DataType> *inNode)
{
    if (inNode == nullptr) {
        return nullptr;
    }
    else if (inNode->right == nullptr) {
        return inNode;
    }
    else {
        return FindMaxCore(inNode->right);
    }
}

递归查找最大元素

template <typename DataType>
Node<DataType> * BinarySearchTree<DataType>::FindMax()
{
    if (root == nullptr) {
        return nullptr;
    }
    return FindMaxCore(root);
}

非递归查找最大元素

template <typename DataType>
Node<DataType> * BinarySearchTree<DataType>::FindMaxNon()
{
    if (root == nullptr) {
        return nullptr;
    }
    Node<DataType> *pre = root;
    Node<DataType> *cycleIter = pre->right;
    while (cycleIter != nullptr) {
        pre = cycleIter;
        cycleIter = pre->right;
    }
    return pre;
}

递归删除节点函数核心元素

template <typename DataType>
Node<DataType> * BinarySearchTree<DataType>::DeleteElementCore(Node<DataType> *inNode, DataType inElement)
{
    if (inNode == nullptr) {
        return nullptr;
    }
    else if (inNode->data > inElement) {
        inNode->left = DeleteElementCore(inNode->left, inElement);
    }
    else if (inNode->data < inElement) {
        inNode->right = DeleteElementCore(inNode->right, inElement);
    }
    else {
        if (inNode->left != nullptr && inNode->right != nullptr) {
            Node<DataType> *tmp = FindMinCore(inNode->right);
            inNode->data = tmp->data;
            inNode->right = DeleteElementCore(inNode->right, inNode->data);
        }
        else {
            Node<DataType> *tmp = inNode;
            if (inNode->left == nullptr) {
                inNode = inNode->right;
            }
            else {
                inNode = inNode->left;
            }
            delete  tmp;
        }
    }
    return inNode;
}

相关文章

  • 详谈树结构(传统树、字典树、hash 树、Merkle Patr

    关于数据结构中树结构的相关分享 本文参考: 树结构参考文献 一、传统的数据结构中的树结构 树结构是一种非线性存储结...

  • JavaScript 数据结构之二叉搜索树

    一、认识树结构 树结构示意图 树结构中的一些术语 树(Tree): n(n>=0) 个节点构成的有限集合 n = ...

  • Element-Ui el-tree 超出部分自动换行

    在使用element-ui 框架做vue 项目树结构时,发现需要固定树结构的宽度,而且树结构的字段有可能会特别长,...

  • MySql_web树结构

    很多网站的分类都是树结构,这里是一个理论上能实现无限级分类的树结构的方法。 创建库表 加入数据 取得树结构:

  • 03-树结构

    树结构依靠节点、叶子节点、子树将自身的数据扩展为像一棵倒过来的树 1. 什么是树结构 树结构依托路径、节点、叶子节...

  • JS树结构操作

    一、遍历树结构 1. 树结构介绍 JS中树结构一般是类似于这样的结构: 为了更通用,可以用存储了树根节点的列表表示...

  • 树结构

    树结构 动态查找树主要有: 二叉查找树(Binary Search Tree), 平衡二叉查找树(Balanced...

  • 树结构

    树:层次关系Tree :n个节点构成的有限集合;n=0时;称为空树;对于非空树,具备特质有: 树中有一个根的特殊节...

  • 树结构

    1.树结构展示 必选属性有:没有 id:tree_【展示内容的介绍...

  • 树结构

    树的内部节点:非叶子节点树的外部节点:叶子节点 如何计算一个树的深度和高度 计算树的深度 假设p是树t中的一个节点...

网友评论

      本文标题:树结构

      本文链接:https://www.haomeiwen.com/subject/uysmfftx.html