------------------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】

原文地址:https://www.cnblogs.com/siwuxie095/p/6834756.html