zoukankan      html  css  js  c++  java
  • ------------------siwuxie095

       

       

       

       

       

       

       

       

       

       

    这里介绍 ,那么什么是树呢?

       

       

       

       

    这个问题太简单了,这就是一棵树,如下:

       

       

       

       

       

    数据结构中的树,与生活中的树有几分类似,但不完全相同

       

    树的定义:树是节点有限集合

       

       

       

       

    上面的概念看上去还是云遮雾罩的,那实际的树是什么样的呢?如下:

       

       

       

    左边是一棵正着的树,它有一个节点 A,它向下分出一些分节点,

    分节点又向下分出更多的分节点

       

    对于这棵树来说,第一:它是有限集合,第二:每一个元素都

    是一个节点

       

       

    如果把这棵树倒过来,就是右边的那棵树的样子,A 节点看成树

    根,它上面的节点就是一个一个的叶子

       

       

       

    树的诸多概念:

       

       

       

    作为一棵树来说,最顶端的节点,称之为 根节点

       

    显然,对于根节点 A 来说,它肯定是双亲,作为双亲来说,

    它下面就有孩子,分别是 B、C、D

       

    而对于 B 结点来说,它的孩子就是 E 和 F,对于 D 结点来

    说,它的孩子就是 G 和 H,对于 C 结点来说,它没有孩子

       

       

    注意:双亲不是两个节点,而是一个结点。叫法上可能感觉

    有些奇异,也可以把 双亲 叫做 父节点

       

       

       

    这个名词在树中是用的非常多的,什么是度呢?

       

    如下:

       

    A 节点的度为 3,因为它有三个孩子,分别是 B、C、D

       

    B 节点的度是 2,因为它有两个孩子,分别是 E 和 F

       

    C 节点的度为 0,因为它没有孩子

       

    D 节点的度为 2,因为它有两个孩子,分别是 G 和 H

       

    E、F、G、H 这 4 个节点的度都为 0,因为它们都没有孩子

       

       

    可见: 就是当前节点下直接的孩子数

       

       

       

    叶子:对于一棵树来说,终端节点就是叶子,如:E、F、G、H、C

       

    :根是相对于叶子来说的,非终端节点就是根,如:A、B、D

       

    「相对于整棵树来说,会有子树的概念,所以,根 也是相对而言的,

    但一般情况下,还是称总的根节点为 根节点,其他根节点为 父节点

       

       

       

    有序树无序树 是一个相对的概念,如下:

       

    对于由 B、E、F 构成的子树而言:

       

    B 节点有两个孩子,分别是 E 和 F,如果 E 和 F 不可以换顺序,

    那么它就是一棵有序的树,如果 E 和 F 可以随便换顺序,而不

    影响逻辑,那么它就是一颗无序的树

       

       

       

    祖先 子孙:

       

    对于祖先来说,如下:

       

    对于 E 节点来说,它的祖先就是 B 节点 和 A 节点,

    对于 G 节点来说,它的祖先就是 D 节点 和 A 节点

       

    可见:祖先,即 指定当前节点之后,它向上一直到总的根节点,

    所路过的所有节点,都是它的祖先

       

       

    对于子孙来说,如下:

       

    对于 A 节点来说,下面所有的节点都是它的子孙,

    对于 D 节点来说,G 节点 和 H 节点是它的子孙

       

    可见:子孙,即 指定当前节点之后,只要是从这个节点开始,向

    下分出的节点,以及分出节点的子节点,都是它的子孙

       

       

       

    除了上述概念之外,还需要介绍如下概念:

       

       

       

       

    依然是上面那棵树,它分为三层:第一层、第二层、第三层

       

    深度 分为 节点深度 树的深度

       

       

    对于 节点深度 来说,它和它所在是统一的

       

    如下:

       

    在第一层的节点,它的深度就为 1

    在第二层的节点,它的深度就为 2

    在第三层的节点,它的深度就为 3

       

       

    树的深度 是指当前这棵树中,节点所具有的最大深度

       

    对于这棵树来说,它最多只有三层,那么它的深度就为 3

       

       

       

    森林

       

       

       

    对于树来说,是一棵独立的树,而多棵独立的树放到一起,就是 森林

       

       

    也可以这样理解:

       

    对于同一棵树来说,可以把它看成两棵不同的子树,如果看成两棵不同

    的子树,那么它也组成了一个森林,这有点像孤木成林的感觉

       

       

       

       

       

       

    二叉树

       

       

    所有节点都小于等于 2

       

    如下:

       

       

       

    左边这颗树的根节点,它的度为 3,致使这一整颗树不能称之为 二叉树,

       

    而右边这颗树的所有节点的度要么为 2、要么为 1、要么为 0,所以它是

    一棵二叉树

       

       

       

    二叉树的遍历

       

    二叉树的遍历分为:前序遍历中序遍历后序遍历

       

       

       

    前、中、后 相对于二叉树的来说的,如下:

       

    如果先访问根,再访问左节点,最后访问右节点,就称之为 前序遍历

    如果先访问左节点,再访问根,最后访问右节点,就称之为 中序遍历

    如果先访问左节点,再访问右节点,最后访问根,就称之为 后序遍历

       

       

    所以,可以简记为:

       

    前序遍历: - 左 - 右

    中序遍历: - 根 - 右

    后续遍历: - 右 - 根

       

       

       

       

       

       

    树的用途

       

       

    树的用途很多,如:大家非常熟悉的压缩软件,就可以通过

    赫夫曼树 赫夫曼编码 来实现

       

       

       

       

    而树的一个更让人心驰神往的作用就是能够实现人机对战

       

       

       

       

    大家都听说过 李世石 AlphaGo 这个机器之间的对战,也都玩过

    一些人机对战的象棋游戏,肯定都能够领略到人机对战的乐趣

       

    其实人机对战的过程就是不断地做树的搜索,找到最佳的对战方法,

    从而机器做出最佳的当前选择

       

       

       

       

       

       

       

    程序 1:用数组实现二叉树

       

    Tree.h:

       

    #ifndef TREE_H

    #define TREE_H

       

       

    //用数组实现的二叉树

    class Tree

    {

    public:

    Tree(int size,int *pRoot); //创建树

    ~Tree(); //销毁树

    //根据索引寻找节点

    int *SearchNode(int nodeIndex);

    //添加节点

    bool AddNode(int nodeIndex, int direction, int *pNode);

    //删除节点

    bool DeleteNode(int nodeIndex, int *pNode);

    //遍历节点

    void TreeTraverse();

    private:

    int *m_pTree; //指向二叉树的指针

    int m_iSize; //表示二叉树的数组的大小

    };

       

    #endif

       

       

       

    Tree.cpp:

       

    #include "Tree.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

       

    Tree::Tree(int size, int *pRoot)

    {

    m_iSize = size;

    m_pTree = new int[m_iSize];

    //将数组中每个元素都初始化为0

    for (int i = 0; i < m_iSize; i++)

    {

    m_pTree[i] = 0;

    }

    //再初始化根节点

    m_pTree[0] = *pRoot;

    }

       

       

    Tree::~Tree()

    {

    delete[]m_pTree;

    m_pTree = NULL;

    }

       

       

    //根据索引,也就是数组下标进行搜索

    int *Tree::SearchNode(int nodeIndex)

    {

    //判断nodeIndex是否合法

    if (nodeIndex < 0 || nodeIndex >= m_iSize)

    {

    return NULL;

    }

    //如果传进来的索引对应的值为0

    //说明当前结点本身也没有意义

    if (m_pTree[nodeIndex] == 0)

    {

    return NULL;

    }

    return &m_pTree[nodeIndex];

    }

       

       

    bool Tree::AddNode(int nodeIndex, int direction, int *pNode)

    {

    if (nodeIndex < 0 || nodeIndex >= m_iSize)

    {

    return false;

    }

    if (m_pTree[nodeIndex] == 0)

    {

    return false;

    }

       

    //定义direction:

    //值为0,则插入nodeIndex处结点的左孩子处

    //值为1,则插入nodeIndex处结点的右孩子处

    if (direction == 0)

    {

    //如果超出范围,返回false

    if (nodeIndex * 2 + 1 >= m_iSize)

    {

    return false;

    }

    //如果已经有值存在,返回false

    if (m_pTree[nodeIndex * 2 + 1] != 0)

    {

    return false;

    }

    m_pTree[nodeIndex * 2 + 1] = *pNode;

    }

    if (direction == 1)

    {

    //如果超出范围,返回false

    if (nodeIndex * 2 + 2 >= m_iSize)

    {

    return false;

    }

    //如果已经有值存在,返回false

    if (m_pTree[nodeIndex * 2 + 2] != 0)

    {

    return false;

    }

    m_pTree[nodeIndex * 2 + 2] = *pNode;

    }

    return true;

    }

       

       

    bool Tree::DeleteNode(int nodeIndex, int *pNode)

    {

    if (nodeIndex < 0 || nodeIndex >= m_iSize)

    {

    return false;

    }

    if (m_pTree[nodeIndex] == 0)

    {

    return false;

    }

    //将对应nodeIndex的值取出来

    *pNode = m_pTree[nodeIndex];

    //删除节点,赋值0即可

    m_pTree[nodeIndex] = 0;

    return true;

    }

       

       

    void Tree::TreeTraverse()

    {

    for (int i = 0; i < m_iSize; i++)

    {

    cout << m_pTree[i] << " ";

    }

    }

       

       

       

    main.cpp:

       

    #include "Tree.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    /**********************************************/

    /*

    二叉树(数组表示):使用数组的方式来表达二叉树

       

    关于数组与树之间的算法转换:

       

    int tree[n] 3 5 8 2 6 9 7

       

    ----------------3(0)

    --------5(1)------------8(2)

    ----2(3)----6(4)----9(5)----7(6)

       

    把数组看成二叉树:

    3 的左右孩子是 5 8

    5 的左右孩子是 2 6

    8 的左右孩子是 9 7

       

    父亲节点下标*2+1,即该节点左孩子

    父亲节点下标*2+2,即该节点右孩子

       

    一个节点,如果它只有右孩子而没有左孩子,就在左孩子处填0

    同理,如果它只有左孩子而没有右孩子,就在右孩子处填0

       

    0 来表达当前位置上不存在节点的情况

    */

    /**********************************************/

    int main(void)

    {

    int root = 3;

    Tree *p = new Tree(10, &root);

       

    int n1 = 5;

    int n2 = 8;

    p->AddNode(0, 0, &n1);

    p->AddNode(0, 1, &n2);

       

    int n3 = 2;

    int n4 = 6;

    p->AddNode(1, 0, &n3);

    p->AddNode(1, 1, &n4);

       

    int n5 = 9;

    int n6 = 7;

    p->AddNode(2, 0, &n5);

    p->AddNode(2, 1, &n6);

       

    p->TreeTraverse();

       

    int node = 0;

    p->DeleteNode(6, &node);

    cout << endl << "delNode:" << node << endl;

       

    p->TreeTraverse();

       

    int *temp = p->SearchNode(2);

    cout << endl << "searchNode:" << *temp << endl;

       

    delete p;

    p = NULL;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

       

       

       

    程序 2:用链表实现二叉树

       

    Node.h:

       

    #ifndef NODE_H

    #define NODE_H

       

    //节点五要素:索引、数据、左孩子指针、右孩子指针、父节点指针

    class Node

    {

    public:

    Node();

    Node *SearchNode(int nodeIndex);

    void DeleteNode();

    void PreorderTraversal();

    void InorderTraversal();

    void PostorderTraversal();

    int index;

    int data;

    Node *pLChild;

    Node *pRChild;

    Node *pParent;

    };

       

       

    #endif

       

       

       

    Node.cpp:

       

    #include "Node.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    Node::Node()

    {

    index = 0;

    data = 0;

    pLChild = NULL;

    pRChild = NULL;

    pParent = NULL;

    }

       

       

    Node *Node::SearchNode(int nodeIndex)

    {

    //先要看看当前节点是不是要找的节点

    if (this->index == nodeIndex)

    {

    return this;

    }

       

    Node *temp = NULL;

       

    //如果不是,就要看当前节点的左右孩子

    if (this->pLChild != NULL)

    {

    if (this->pLChild->index == nodeIndex)

    {

    return this->pLChild;

    }

    else

    {

    temp = this->pLChild->SearchNode(nodeIndex);

    if (temp != NULL)

    {

    //temp不为空才返回

    return temp;

    }

    }

    }

       

    if (this->pRChild != NULL)

    {

    if (this->pRChild->index == nodeIndex)

    {

    return this->pRChild;

    }

    else

    {

    temp = this->pRChild->SearchNode(nodeIndex);

    //到了下面,不管temp是否为空,都返回

    return temp;

    }

    }

    //树中没有这个节点,返回NULL

    return NULL;

    }

       

       

    void Node::DeleteNode()

    {

    //将当前节点的左右孩子先干掉

    if (this->pLChild != NULL)

    {

    //如果不为空,继续执行DeleteNode(),迭代递归

    this->pLChild->DeleteNode();

    }

    if (this->pRChild != NULL)

    {

    //如果不为空,继续执行DeleteNode(),迭代递归

    this->pRChild->DeleteNode();

    }

    //找到当前节点的父节点,前提是父节点不为NULL

    if (this->pParent != NULL)

    {

    //比较父节点的左孩子是不是与当前节点相同

    //如果相同,当前节点是父节点的左孩子

    if (this->pParent->pLChild == this)

    {

    //将自己父节点的左孩子指针置为NULL

    this->pParent->pLChild = NULL;

    }

    //同理

    if (this->pParent->pRChild == this)

    {

    this->pParent->pRChild = NULL;

    }

    }

       

    //删掉当前节点:

    //子节点删除完毕,父节点指向当前节点的指针赋空,

    //最后当前节点自杀

    delete this;

    }

       

       

    //前序遍历:根左右

    void Node::PreorderTraversal()

    {

    //先访问自己,再访问自己的左孩子,再访问自己的右孩子

    cout << this->index << " " << this->data << endl;

    if (this->pLChild != NULL)

    {

    this->pLChild->PreorderTraversal();

    }

    if (this->pRChild != NULL)

    {

    this->pRChild->PreorderTraversal();

    }

    }

       

       

    //中序遍历:左根右

    void Node::InorderTraversal()

    {

    if (this->pLChild != NULL)

    {

    this->pLChild->InorderTraversal();

    }

       

    cout << this->index << " " << this->data << endl;

       

    if (this->pRChild != NULL)

    {

    this->pRChild->InorderTraversal();

    }

    }

       

       

    //后序遍历:左右根

    void Node::PostorderTraversal()

    {

    if (this->pLChild != NULL)

    {

    this->pLChild->PostorderTraversal();

    }

    if (this->pRChild != NULL)

    {

    this->pRChild->PostorderTraversal();

    }

    cout << this->index << " " << this->data << endl;

    }

       

       

       

    Tree.h:

       

    #ifndef TREE_H

    #define TREE_H

       

    #include "Node.h"

       

       

    //用链表实现的二叉树

    class Tree

    {

    public:

    Tree(); //创建树(1

    ~Tree(); //销毁树(5

    //根据索引寻找节点(2

    Node *SearchNode(int nodeIndex);

    //添加节点(3

    bool AddNode(int nodeIndex, int direction, Node *pNode);

    //删除节点(4

    bool DeleteNode(int nodeIndex, Node *pNode);

    void PreorderTraversal(); //前序遍历(6

    void InorderTraversal(); //中序遍历(7

    void PostorderTraversal(); //后序遍历(8

    private:

    Node *m_pRoot;

    };

       

    //树中很多函数的实现都转化为了节点函数的实现

    //即将树级的实现转化为节点级,更容易理解

    #endif

       

       

       

    Tree.cpp:

       

    #include "Tree.h"

    #include "stdlib.h"

       

       

    //1

    Tree::Tree()

    {

    m_pRoot = new Node();

    }

       

       

    //5

    Tree::~Tree()

    {

    //传入根节点索引0,同时不再取出数据

    DeleteNode(0, NULL);

       

    //或直接使用根节点删除,均可

    //m_pRoot->DeleteNode();

    //如果Tree中除了m_pRoot之外,还有其他数据成员,

    //并且是指针,而且在构造函数中还申请了内存

    //

    //上面的两种调用就不够了,还需要释放其他的内存

    }

       

       

    //2

    Node *Tree::SearchNode(int nodeIndex)

    {

    return m_pRoot->SearchNode(nodeIndex);

    }

       

       

    //3

    bool Tree::AddNode(int nodeIndex, int direction, Node *pNode)

    {

       

    //先找到要挂载的目标节点

    Node *temp = SearchNode(nodeIndex);

    if (NULL == temp)

    {

    return false;

    }

       

    //将传入参数pNode里的数据拷贝出来,因为如果直接将

    //pNode挂载到树的下边,pNode作为外边传进来的数据,

    //它如果被外边的函数或其它的语句修改,那它的完整

    //性就不复存在了,对于树的影响也是非常大的,往往

    //会造成一些致命错误

    Node *node = new Node();

    if (NULL == node)

    {

    return false;

    }

    node->index = pNode->index;

    node->data = pNode->data;

    node->pParent = temp;

       

    //定义direction0,表示挂载到左边,为1,表示挂载到右边

    if (direction == 0)

    {

    temp->pLChild = node;

    }

    if (direction == 1)

    {

    temp->pRChild = node;

    }

       

    return true;

    }

       

       

    //4

    //删除指定节点,但在树中要删除指定节点并不是那么容易,

    //除了要删除指定节点之外,还有与其相连的子节点,以及

    //子节点的子节点...也必须都要删除掉

    bool Tree::DeleteNode(int nodeIndex, Node *pNode)

    {

    //先找到要删除的目标节点

    Node *temp = SearchNode(nodeIndex);

    if (NULL == temp)

    {

    return false;

    }

       

    //如果pNodeNULL,就不取temp中的数据了

    //只作删除操作即可

    if (pNode != NULL)

    {

    //主要取data

    pNode->data = temp->data;

    }

    //删除temp及其子节点

    temp->DeleteNode();

    return true;

    }

       

       

    //6

    void Tree::PreorderTraversal()

    {

    m_pRoot->PreorderTraversal();

    }

       

       

    //7

    void Tree::InorderTraversal()

    {

    m_pRoot->InorderTraversal();

    }

       

       

    //8

    void Tree::PostorderTraversal()

    {

    m_pRoot->PostorderTraversal();

    }

       

       

       

    main.cpp:

       

    #include "Tree.h"

    #include "stdlib.h"

       

    /*********************************************/

    /*

    二叉树:链表实现

    节点要素:索引、数据、左孩子指针、右孩子指针、父节点指针

    (数据数据域,左孩子指针、右孩子指针、父节点指针指针域)

       

    -----------------(0)

    --------5(1)------------8(2)

    ----2(3)----6(4)----9(5)----7(6)

       

    索引的遍历,如下:

    前序遍历:0 1 3 4 2 5 6

    中序遍历:3 1 4 0 5 2 6

    后序遍历:3 4 1 5 6 2 0

       

    创建一棵树时,只需要将第一个根节点创建出来即可,

    根节点的数据域可以没有任何意义,但它的指针域一

    定要先指定左右两边,初始状态下为 NULL

       

    当根节点挂载相应的左孩子或右孩子时,再为它的左

    右指针分别赋相应的值

    */

    /*********************************************/

    int main(void)

    {

    Tree *tree = new Tree();

       

    Node node1;

    node1.index = 1;

    node1.data = 5;

       

    Node node2;

    node2.index = 2;

    node2.data = 8;

       

    Node node3;

    node3.index = 3;

    node3.data = 2;

       

    Node node4;

    node4.index = 4;

    node4.data = 6;

       

    Node node5;

    node5.index = 5;

    node5.data = 9;

       

    Node node6;

    node6.index = 6;

    node6.data = 7;

       

       

    tree->AddNode(0, 0, &node1);

    tree->AddNode(0, 1, &node2);

    tree->AddNode(1, 0, &node3);

    tree->AddNode(1, 1, &node4);

    tree->AddNode(2, 0, &node5);

    tree->AddNode(2, 1, &node6);

       

    tree->DeleteNode(6, NULL);

       

    //tree->PreorderTraversal();

    //tree->InorderTraversal();

    tree->PostorderTraversal();

       

    delete tree;

    tree = NULL;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

       

       

       

       

    【made by siwuxie095】

  • 相关阅读:
    转:【实用教程】阿里云服务器的配置和使用
    C# 定制错误页面
    C# Session进程外存储
    NOIP200101数的计算
    周末舞会
    queue 队列
    信息学作文
    求三个数的平均数
    Hello world
    Django-Form组件-forms.Form
  • 原文地址:https://www.cnblogs.com/siwuxie095/p/6834756.html
Copyright © 2011-2022 走看看