当前位置: 首页 > news >正文

数据结构系列之二叉树


前言

二叉树是一棵比较重要的树了,后面堆、搜索二叉树、红黑树、AVL树、甚至算法里的线段树,它们的基础都是二叉树,所以二叉树比较重要,需要掌握


一、什么是二叉树

二叉树是一颗树,可以为空,由左子树,右子树,根组成, 二叉树不存在度大于2的节点,当然,左子树和右子树都可以不存。

一些特殊的树:
1.满二叉树: 除了叶子节点,每一层的节点都有左右孩子,这就是满二叉树,深度为h,一层的节点个数为2 ^ h.
在这里插入图片描述
2.完全二叉树
堆的结构里提到了,每一个节点都能与深度相同的满二叉树中1到n的节点相对,就是完全二叉树,满二叉树也是一种完全二叉树

二、一些性质

1.对于任何一棵二叉树,如果它的度为0的节点个数为n0,度为2的节点的个数为n2,则n0 = n2 + 1
这里简单推导一下,设度为0,1,2的节点个数分别为n0,n1,n2,我们知道树的性质有一条是节点的个数 = 度数 + 1,所以: n0 + n1 + n2 = 0 * n0 + 1 * n1 + 2 * n2 + 1,推导出n0 = n2 + 1

还有一些简单性质想一想就明白了,比如第i层的节点个数最多是多少,n个节点的满二叉树的深度是多少… … …懒得说了

2.对于完全二叉树用i开始编号,则有:
i == 0,为根节点
i >0 , i节点父亲的序号, (i - 1) / 2,
如果2 * i + 1 < n ,左孩子的序号, 反之没有左孩子
如果2 * i + 2 < n ,右孩子的序号, 反之没有右孩子


三、二叉树的存储结构

二叉树可以分为两种结构存储,一种是顺序结构,一种是链式结构

1.顺序存储

顺序存储就是使用数组来存储,一般只适用于完全二叉树,也就是堆可以使用,对于非完全二叉树不适用,不适用的原因就是空间浪费太多。

2.链式存储

就是上次树中提到的二叉链和三叉链

//二叉链 
struct Node{struct Node* _firstchild; //左孩子  struct Node* _rightchild; //右孩子  int _val; //这里可以任意类型,换成宏就可以 
};//三叉链 
struct Node{struct Node* _parent;     //父亲 struct Node* _firstchild; //左孩子  struct Node* _rightchild; //右孩子  int _val; //这里可以任意类型,换成宏就可以 
};

四、二叉树的操作

想要操作首先就需要创建一棵二叉树,怎么创建???这里我们先手动创建,真正的方式其实是前序来创建,销毁也是后序来销毁,介绍完前后序再来。

手动创建二叉树

这里返回的节点就是根节点

BTNode* BuyNode(BTDATATYPE x)
{BTNode* node = (BTNode*)malloc(sizeof(BTNode));if (node == NULL){perror("malloc");return NULL;}node->data = x;node->left = NULL;node->right = NULL;return node;
}
BTNode* CreateTree()
{ BTNode* node1 = BuyNode(1);BTNode* node2 = BuyNode(2);BTNode* node3 = BuyNode(3);BTNode* node4 = BuyNode(4);BTNode* node5 = BuyNode(5);BTNode* node6 = BuyNode(6);BTNode* node7 = BuyNode(7);node1->left = node2;node1->right = node4;node2->left = node3;node4->left = node5;node4->right = node6;node3->left = node7;return node1;}

前序遍历、中序遍历、后序遍历

堆里介绍了,前序就是以根、左子树、右子树的方式来遍历,中序就是左子树、根、右子树,后序就是左子树、右子树、根。 其中我们采用递归使用比较简单,有两个遍历后的结果就可以复原树(不可以缺少中序).

前序
void PrevOrder(BTNode *root)
{if (root == NULL){printf("NULL ");return;}printf("%d ", root->data);PrevOrder(root->left);PrevOrder(root->right);
}
中序
void InOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}InOrder(root->left);printf("%d ", root->data);InOrder(root->right);
}
后序
void PostOrder(BTNode* root)
{if (root == NULL){printf("NULL ");return;}PostOrder(root->left);PostOrder(root->right);printf("%d ", root->data);
}

层序遍历

层序遍历就是一层一层遍历,这里我们用到了队列,用下面这棵树介绍一下操作
在这里插入图片描述
如果根不为空,队列入A,直到队列的size为0为止,取出front打印值并front掉,如果front的左子树存在,入队,front的右子树存在,入右子树。

void LevelOrder(BTNode* root)
{queue<BTNode*> q;if(root != nullptr) {q.push(root);}while(q.size()){BTNode* t = q.front();q.pop();cout << t->data << ' ';if(t->left) q.push(t->left);if(t->right)q.push(t->right);	} }

如果想一层一层的打印出来怎么办???就是打印一层然后换行,控制一下出的节点个数就好了,每一次拿到q.size之后,控制一次就出q.size次,操作完更新q.size,就是套一层for循环就好了,在while里面套一层for

void LevelOrder(BTNode* root)
{queue<BTNode*> q;if(root != nullptr) {q.push(root);}int qsize = 1;while(q.size()){for(int i = 0;i < qsize;++i){BTNode* t = q.front();q.pop();cout << t->data << ' ';if(t->left) q.push(t->left);if(t->right)q.push(t->right);}cout << endl;qsize = q.size(); } }

前序遍历创建二叉树

创建出来什么树完全会按照递归的逻辑来,随机插入节点等等。
为什么用前序遍历来创建?????先看代码再解释.

这里我认为中序或者后序无法创建二叉树,因为无法确定根节点,只有前序可以。如果有意见不同者可以评论

创建二叉树

void PrevCreateTree(BTNode*& node)
{char ch;do {ch = getchar();} while (isspace(ch)); if(ch == '#') node = nullptr; else{node = BuyNode(ch - '0');printf("输入左子树的值,输入#表示左子树为空\n");PrevCreateTree(node->left);printf("输入右子树的值,输入#表示右子树为空\n");PrevCreateTree(node->right);}
}

前序构建完全二叉树,n就表示一共几个节点,像上面那样改也可以,但是这个代码构建出来的顺序和自己输入的不同

void PrevCreateTree(BTNode*& node,int n)
{if(n == 0){node = nullptr;return ;}int x;cin >> x;node = BuyNode(x);int m = n - 1;int left_m = m / 2;int right_m = m - left_m;PrevCreateTree(node->left,left_m);PrevCreateTree(node->right,right_m);
}

求高度

高度还是很简单的,求出左右子树的高度,哪个高返回哪个加一,转换成子问题,用递归。

int TreeHeight(BTNode* root)
{if (root == nullptr){return 0;}int left = TreeHeight(root->left);int right = TreeHeight(root->right);return left > right ? left + 1 : right + 1;
}

求size

size和高度的逻辑差不多,就是存在就++

int TreeSize(BTNode* root)
{return root == nullptr ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}

这样也行,看着和高度的逻辑一样

int TreeSize(BTNode* root)
{if(root == nullptr){return 0;}int leftSize = TreeSize(root->left);int rightSize = TreeSize(root->right);return leftSize + rightSize + 1;
}

查找值为x的节点

核心逻辑就是先看看根是不是,根不是就看左子树,左子树不是就看右子树

BTNode* Find(BTNode* root, int x)
{if (root == nullptr){return nullptr;}if (root->data == x){return root;}BTNode* lret = Find(root->left, x);if (lret){return lret;}BTNode* rret = Find(root->right, x);if (rret){return rret;}return nullptr;
}

根的第K层个数

核心逻辑:根的第k层个数=根的左子树的第k-1层个数+根的右子树的第k-1层个数,还是很好理解的,根的左子树多了一层所以就是左子树的k - 1层,注意k == 1表示只有第一层,一定是1

int TreeKLevel(BTNode* root, int k)
{if (root == NULL){return 0;}if (k == 1){return 1;}return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
}

删除

删除的逻辑就是和创建相反就可以,后序删除就行。

void Destroy(BTNode*root)
{if(root == nullptr){return ;}Destroy(root->left);Destroy(root->right);cout << root->data <<  "被销毁"<<endl; free(root);
}

总结

二叉树的东西很多,特别是递归的玩法让人很不适应,需要掌握,下次直接更新所有的排序算法。

http://www.dtcms.com/a/517583.html

相关文章:

  • 【数据结构】反射、枚举、lambda表达式以及补充知识
  • 网站开发费计入什么科目自己做微信电影网站怎么做
  • 23.C++11(三)
  • 梅州市五华县建设银行网站景观设计公司起名
  • 刚做淘客没有网站奥门网站建设
  • 专业做网站机构哪些ppt网站是免费的
  • 爱情动做网站推荐wordpress去除更新
  • 深圳技术支持 骏域网站建设wordpress 导出pdf文件
  • 深圳公明网站建设问题不大工作室 网站
  • Langchain从零开始到应用落地案例[AI智能助手]【1】---调用ollama模型实现简单循环会话
  • 【架构】MVP 对比 MVVM
  • 建立网站的基本流程有哪些步骤给wordpress添加小图标
  • Springboot——整合Aspose实现table的字段填充与表格复制
  • 产品展示型网站赏析河南网站建设电话
  • 国外免费搭建网站源码企业网站建设策划书 前言
  • 【网络代理相关知识】
  • 美股及墨西哥股票数据接口文档
  • 做电销哪些网站可以找到客户做网站的北京
  • 网站描述修改做h5那个网站好
  • 什么公司时候做网站厦门编程培训机构
  • 建设网站 买了域名还要什么网站权重分为几个等级
  • 操作系统 进程(3)进程调度算法
  • 建筑设计自学网站做一个平台网站要多少钱
  • 专门做单页的网站把网站制作成app
  • 电子商务网站建设实践报告摘要如何做查询网站
  • HX711电子秤模块详解(STM32)
  • 成都高速公路网站建设招标消防工程师证怎么考
  • 白山北京网站建设大连甘井子区教育公共服务平台
  • SpringBoot考勤管理系统
  • 公司网站站群是什么为什么要建设就业指导网站