树基本概念及用法
树状图是一种数据结构,它是由 n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点外,每个子结点可以分为多个不相交的子树;
专业术语 | 中 文 | 描 述 |
Root | 根节点 | 一棵树的顶点 |
Child | 孩子节点 | 一个结点含有的子树的根结点称为该结点的子结点 |
Leaf | 叶子节点 | 没有孩子的节点(度为0) |
Degree | 度 | 一个节点包含的子树的数量 |
Edge | 边 | 一个节点与另外一个节点的连接 |
Depth | 深度 | 根节点到这个节点经过的边的数量 |
Height | 节点高度 | 从当前节点到叶子节点形成路径中边的数量 |
Level | 层级 | 节点到根节点最长路径的边的总和 |
Path | 路径 | 一个节点和另一个节点之间经过的边和 Node 的序列 |
2.二叉树
2.1二叉树的基本概念
二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(空二叉树),或者由一个根结点和两个互不相交的、分别称为根结点左子树和右子树的二叉树组成。
二叉树是一个每个结点最多只能有两个分支的树,左边的分支称之为左子树,右边的分支称之为右子树。
|
2.2二叉树的特点
|
2.3二叉树的五种形态
1 2 3 4 5 |
|
2.4特殊二叉树
|
|
|
|
|
2.5二叉树的性质
|
3.二叉搜索树的算法实现
比如有以下数据:
当我们想保证查找效率时,可以用顺序表存储,当我们想保证插入和删除效率时,我们可以用链式表存储,有没有一种存储方法可以同时兼顾顺序表和链式表的优点?
使用二叉树,便可兼顾查找效率和插入删除效率~
二叉树一般采用链式存储方式:每个结点包含两个指针域,指向两个孩子结点,还包含一个数据域,存储结点信息。
结点结构体定义:
basic
1 typedef struct _BNode {
2 int data;
3 struct _BNode *lchild, *rchild;
4 }Bnode,*Btree;
3.1二叉搜索树插入结点
xl
1
2 //二叉树插入结点
3 /*将插入结点e,与结点root结点进行比较,若小于则去到左子树,否则
4 去右子树进行比较,重复以上操作直到找到一个空位置放置该结点
5 */
6 bool InsertBtree(Btree* root, Bnode* node) {
7 Bnode* temp = NULL;
8 Bnode* parent = NULL;
9 if (!node) { //如果插入结点为空,返回false
10 return false;
11 }else { //清空插入结点的左右子树
12 node->lchild = NULL;
13 node->rchild = NULL;
14 }
15
16 if (!(*root)) { //如果根节点不存在,将插入结点作为根节点
17 *root = node;
18 return true;
19 }else {
20 temp = *root;
21 }
22
23 while (temp != NULL) {
24 parent = temp;
25 if (temp->data>node->data) { //小于,继续向左
26 temp = temp->lchild;
27 }
28 else { //大于,继续向右(不可以有相同的值)
29 temp=temp->rchild;
30 }
31 //while循环结束,parent所处位置即为要插入结点的父结点
32 }
33 if (node->data < parent->data) {
34 parent->lchild = node;
35 }
36 else {
37 parent->rchild = node;
38 }
39 return true;
40 }
3.2二叉搜索树删除结点
1 |
|
1.删除节点不存在左右子节点,即为叶子节点,直接删除
2.删除节点存在左子节点,不存在右子节点,直接把左子节点替代删除节点
3.删除节点存在右子节点,不存在左子节点,直接把右子节点替代删除节点
4.删除节点存在左右子节点,则取左子树上的最大节点或右子树上的最小节点替换删除节点。
代码实现:
xl
1 //查找左子树最大值
2 int findLeftMax(Btree* root) {
3 /*采用递归方式查找
4 * if (root->rchild == NULL)
5 return root->data;
6 return findMax(root->rchild);
7 */
8 //采用循环查找
9 Btree indexNode = *root;
10 while (indexNode->rchild)
11 indexNode = indexNode->rchild;
12 return indexNode->data;
13 }
14
15
16 //采用递归的方式删除结点
17 /*
18 这种递归方式,是将要修改的结点的一层一层的返回
19 */
20 Btree deleteNode(Btree* root, int value) {
21 Btree compareNode = *root;
22 //节点为空(递归找不到value/根节点为空),直接返回
23 if (!compareNode)return compareNode;
24 //大于
25 if (compareNode->data > value) {
26 //左子树重新被赋值
27 compareNode->lchild = deleteNode(&(compareNode->lchild), value);
28 return compareNode;
29 }
30 //小于
31 else if (compareNode->data < value) {
32 //右子树重新被赋值
33 compareNode->rchild = deleteNode(&(compareNode->rchild), value);
34 return compareNode;
35 }
36 else {//等于
37 Btree temp = NULL;
38 //无左右子节点,直接返回NULL
39 if (compareNode->lchild == NULL && compareNode->rchild == NULL) {
40 delete compareNode;
41 }
42 //有左子树,返回左子树
43 else if (compareNode->lchild && compareNode->rchild == NULL) {
44 temp = compareNode->lchild;
45 delete compareNode;
46 }
47 //有右子树,返回右子树
48 else if (compareNode->rchild && compareNode->lchild == NULL) {
49 temp = compareNode->rchild;
50 delete compareNode;
51 }
52 else {
53 //这里采用左子树最大值替换
54 int leftMax = findLeftMax(&(compareNode->lchild));
55 //最大值替换删除结点的值
56 compareNode->data = leftMax;
57 //将最大值从树中删除
58 compareNode->lchild = deleteNode(&(compareNode->lchild), leftMax);
59 temp= compareNode;
60 }
61 return temp;
62 }
63 }
3.3二叉搜索树查找
xquery
1 // 采用递归方式查找结点
2 /*
3 Bnode* queryByRec(Btree root, int value) {
4 if (root == NULL || root->data==value ){
5 return root;
6 }
7 else if (root->data < value) {
8 return queryByRec(root->lchild, value);
9 }
10 else {
11 return queryByRec(root->rchild, value);
12 }
13 }*/
14
15 // 使用非递归方式查找结点
16
17 Bnode* queryByLoop(Btree root, int value) {
18 while (root != NULL && root->data!=value) {
19 if (root->data>value) {
20 root = root->lchild;
21 }
22 else {
23 root = root->rchild;
24 }
25 }
26 return root;
27 }
3.4二叉搜索树遍历结点
3.4.1前序遍历
|
如上图,前序遍历的结果为 19 7 5 11 15 25 21 61
basic
1 void PreOrderRec(int x)//x为根节点
2 {
3 if (x == 0)return;//若遍历完成,返回函数
4 cout<<x;
5 PreOrderRec(a[x].left);//遍历左孩子
6 PreOrderRec(a[x].right);//遍历右孩子
7 }
3.4.2中序遍历
|
还是这个图,但中序遍历的结果为 5 7 11 15 19 21 25 61
basic
1 void PreOrderRec(int x)//x为根节点
2 {
3 if (x == 0)return;//若遍历完成,返回函数
4 PreOrderRec(a[x].left);//遍历左孩子
5 cout<<x;
6 PreOrderRec(a[x].right);//遍历右孩子
7 }
甚至代码都不需要改,只需改变遍历的顺序
3.4.3后序遍历
|
后序遍历结果为 5 15 11 7 21 61 25 19
同样,代码也不需要改变
basic
1 void PreOrderRec(int x)//x为根节点
2 {
3 if (x == 0)return;//若遍历完成,返回函数
4 PreOrderRec(a[x].left);//遍历左孩子
5 PreOrderRec(a[x].right);//遍历右孩子
6 cout<<x;
7 }
以上就是二叉树的常用操作啦,至于增加、删除节点,就类似于链表的操作,由于本蒟蒻对链表简直就算是白痴,此处就不再详解了.另外,此文摘抄了
|
还没有评论,来说两句吧...