数据结构 二叉树

二叉树

二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点。

特殊类型

  1. 满二叉树:如果一棵二叉树只有度为0的节点和度为2的节点,并且度为0的节点在同一层上,则这棵二叉树为满二叉树。
  2. 完全二叉树:深度为k,有n个节点的二叉树当且仅当其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应时,称为完全二叉树 。
    完全二叉树的特点是叶子节点只可能出现在层序最大的两层上,并且某个节点的左分支下子孙的最大层序与右分支下子孙的最大层序相等或大1。

相关术语

①节点:包含一个数据元素及若干指向子树分支的信息。
②节点的度:一个节点拥有子树的数目称为节点的度。
③叶子节点:也称为终端节点,没有子树的节点或者度为零的节点。
④分支节点:也称为非终端节点,度不为零的节点称为非终端节点 。
⑤树的度:树中所有节点的度的最大值。
⑥节点的层次:从根节点开始,假设根节点为第1层,根节点的子节点为第2层,依此类推,如果某一个节点位于第L层,则其子节点位于第L+1层。
⑦树的深度:也称为树的高度,树中所有节点的层次最大值称为树的深度。
⑧有序树:如果树中各棵子树的次序是先后次序,则称该树为有序树。
⑨无序树:如果树中各棵子树的次序没有先后次序,则称该树为无序树 。
⑩森林:由m(m≥0)棵互不相交的树构成一片森林。如果把一棵非空的树的根节点删除,则该树就变成了一片森林,森林中的树由原来根节点的各棵子树构成 。

性质

性质1:二叉树的第i层上至多有2i-1(i≥1)个节点 。
性质2:深度为h的二叉树中至多含有2h-1个节点。
性质3:若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1 。
性质4:具有n个节点的满二叉树深为log2n+1。
性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:

当i=1时,该节点为根,它无双亲节点 。
当i>1时,该节点的双亲节点的编号为i/2 。
若2i≤n,则有编号为2i的左节点,否则没有左节点 。
若2i+1≤n,则有编号为2i+1的右节点,否则没有右节点。

遍历二叉树

遍历是对树的一种最基本的运算,所谓遍历二叉树,就是按一定的规则和顺序走遍二叉树的所有节点,使每一个节点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个节点转换成为一个线性序列来表示 。

1.先(根)序遍历的递归算法定义:
若二叉树非空,则依次执行如下操作:
⑴ 访问根结点;
⑵ 遍历左子树;
⑶ 遍历右子树。
2.中(根)序遍历的递归算法定义:
若二叉树非空,则依次执行如下操作:
⑴遍历左子树;
⑵访问根结点;
⑶遍历右子树。
3.后(根)序遍历得递归算法定义:
若二叉树非空,则依次执行如下操作:
⑴遍历左子树;
⑵遍历右子树;
⑶访问根结点。

线索二叉树

按照某种遍历方式对二叉树进行遍历,可以把二叉树中所有节点排列为一个线性序列。在该序列中,除第一个节点外,每个节点有且仅有一个直接前驱节点;除最后一个节点外,每个节点有且仅有一个直接后继节点。但是,二叉树中每个节点在这个序列中的直接前驱节点和直接后继节点是什么,二叉树的存储结构中并没有反映出来,只能在对二叉树遍历的动态过程中得到这些信息。为了保留节点在某种遍历序列中直接前驱和直接后继的位置信息,可以利用二叉树的二叉链表存储结构中的那些空指针域来指示。这些指向直接前驱节点和指向直接后继节点的指针被称为线索(thread),加了线索的叉树称为线索二叉树 。

刷题

1.【问题描述】已知二叉树的先序遍历序列和中序遍历序列(二叉树中元素类型为字符类型,元素个数不超过20),输出该二叉树的后序遍历序列,并输出该二叉树的高度和叶子节点数

#include <iostream>
#include <string>using namespace std;struct TreeNode
{char val;TreeNode *left;TreeNode *right;TreeNode(char x) : val(x), left(NULL), right(NULL) {}
};int findInorderIndex(const string &inorder, int start, int end, char target)
{for (int i = start; i <= end; ++i){if (inorder[i] == target){return i;}}return -1;
}TreeNode *buildTree(const string &preorder, int &preIndex,const string &inorder, int inStart, int inEnd)
{ if (preIndex >= preorder.length() || inStart > inEnd){return NULL;}char rootValue = preorder[preIndex];TreeNode *root = new TreeNode(rootValue);preIndex++;int rootIndex = findInorderIndex(inorder, inStart, inEnd, rootValue);root->left = buildTree(preorder, preIndex, inorder, inStart, rootIndex - 1);root->right = buildTree(preorder, preIndex, inorder, rootIndex + 1, inEnd);return root;
}string buildPostOrder(TreeNode *root)
{if (!root){return "";}return buildPostOrder(root->left) + buildPostOrder(root->right) + root->val;
}int treeHeight(TreeNode *root)
{if (!root){return 0;}int leftHeight = treeHeight(root->left);int rightHeight = treeHeight(root->right);return 1 + max(leftHeight, rightHeight);
}int leafNodeCount(TreeNode *root)
{if (!root){return 0;}if (!root->left && !root->right){return 1;}return leafNodeCount(root->left) + leafNodeCount(root->right);
}int main()
{string preorder, inorder;cin >> preorder;cin >> inorder;int preIndex = 0;TreeNode *root = buildTree(preorder, preIndex, inorder, 0, inorder.length() - 1);string postorder = buildPostOrder(root);cout << postorder << endl;int height = treeHeight(root);int leafNodeCountValue = leafNodeCount(root);cout << height<<" "<<leafNodeCountValue<<endl;return 0;
}

2.【问题描述】已知二叉树的后序遍历序列和中序遍历序列(二叉树中元素类型为字符类型),输出该二叉树的层次遍历序列。

#include <iostream>
#include <queue>using namespace std;struct TreeNode
{char val;TreeNode *left;TreeNode *right;TreeNode(char x) : val(x), left(NULL), right(NULL) {}
};int findInorderIndex(const string &inorder, int start, int end, char target)
{for (int i = start; i <= end; ++i){if (inorder[i] == target){return i;}}return -1;
}TreeNode *buildTree(const string &inorder, const string &postorder,int inStart, int inEnd, int postStart, int postEnd)
{if (inStart > inEnd || postStart > postEnd){return NULL;}char rootValue = postorder[postEnd];TreeNode *root = new TreeNode(rootValue);int rootIndex = findInorderIndex(inorder, inStart, inEnd, rootValue);int leftSubtreeSize = rootIndex - inStart;root->left = buildTree(inorder, postorder, inStart, rootIndex - 1,postStart, postStart + leftSubtreeSize - 1);root->right = buildTree(inorder, postorder, rootIndex + 1, inEnd,postStart + leftSubtreeSize, postEnd - 1);return root;
}void levelOrderTraversal(TreeNode *root)
{if (!root){return;}queue<TreeNode *> q;q.push(root);while (!q.empty()){TreeNode *current = q.front();q.pop();cout << current->val;if (current->left){q.push(current->left);}if (current->right){q.push(current->right);}}
}int main()
{string inorder, postorder;cin >> postorder;cin >> inorder;TreeNode *root = buildTree(inorder, postorder, 0, inorder.length() - 1,0, postorder.length() - 1);levelOrderTraversal(root);cout << endl;return 0;
}
  1. 后序遍历的非递归方式实现
#include <iostream>
#include <stack>
#include <string>using namespace std;struct TreeNode
{char val;TreeNode *left;TreeNode *right;TreeNode(char x) : val(x), left(NULL), right(NULL) {}
};TreeNode *buildTree(const string &preorder, const string &inorder,int preStart, int preEnd, int inStart, int inEnd)
{if (preStart > preEnd || inStart > inEnd){return NULL;}char rootValue = preorder[preStart];TreeNode *root = new TreeNode(rootValue);int rootIndex;for (rootIndex = inStart; rootIndex <= inEnd; ++rootIndex){if (inorder[rootIndex] == rootValue){break;}}int leftSubtreeSize = rootIndex - inStart;root->left = buildTree(preorder, inorder, preStart + 1, preStart + leftSubtreeSize,inStart, rootIndex - 1);root->right = buildTree(preorder, inorder, preStart + leftSubtreeSize + 1, preEnd,rootIndex + 1, inEnd);return root;
}string postorderTraversal(TreeNode *root)
{string result;if (!root){return result;}stack<TreeNode *> s;TreeNode *lastVisited = NULL;while (root || !s.empty()){while (root){s.push(root);root = root->left;}TreeNode *topNode = s.top();if (topNode->right && topNode->right != lastVisited){root = topNode->right;}else{result += topNode->val;s.pop();lastVisited = topNode;}}return result;
}int main()
{string preorder, inorder;cin >> preorder;cin >> inorder;TreeNode *root = buildTree(preorder, inorder, 0, preorder.length() - 1,0, inorder.length() - 1);string postorder = postorderTraversal(root);cout << postorder << endl;return 0;
}
  1. 二叉树先序遍历非递归实现
#include <iostream>
#include <stack>
#include <string>using namespace std;struct TreeNode
{char val;TreeNode *left;TreeNode *right;TreeNode(char x) : val(x), left(NULL), right(NULL) {}
};TreeNode *buildTree(const string &inorder, const string &postorder,int inStart, int inEnd, int postStart, int postEnd)
{if (inStart > inEnd || postStart > postEnd){return NULL;}char rootValue = postorder[postEnd];TreeNode *root = new TreeNode(rootValue);int rootIndex;for (rootIndex = inStart; rootIndex <= inEnd; ++rootIndex){if (inorder[rootIndex] == rootValue){break;}}int leftSubtreeSize = rootIndex - inStart;root->left = buildTree(inorder, postorder, inStart, rootIndex - 1,postStart, postStart + leftSubtreeSize - 1);root->right = buildTree(inorder, postorder, rootIndex + 1, inEnd,postStart + leftSubtreeSize, postEnd - 1);return root;
}string preorderTraversal(TreeNode *root)
{string result;if (!root){return result;}stack<TreeNode *> s;s.push(root);while (!s.empty()){TreeNode *current = s.top();s.pop();result += current->val;if (current->right){s.push(current->right);}if (current->left){s.push(current->left);}}return result;
}int main()
{string postorder, inorder;cin >> postorder;cin >> inorder;TreeNode *root = buildTree(inorder, postorder, 0, inorder.length() - 1,0, postorder.length() - 1);string preorder = preorderTraversal(root);cout << preorder << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/162963.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【8】Spring Boot 3 集成组件:安全组件 spring security【官网概念篇】

目录 【8】Spring Boot 3 集成组件&#xff1a;安全组件 spring securitySpring Security 简介先决条件引入依赖身份验证密码存储密码存储历史DelegatingPasswordEncoder密码存储格式密码加解密类自定义密码存储 体系结构 ArchitectureServlet 过滤器DelegatingFilterProxyFilt…

第二十章 多线程

20.2创建线程 20.2.1继承Thread类 Thread类是Java.lang包中的一个类&#xff0c;从这个类中实例化的对象代表线程&#xff0c;程序员启动一个新线程需要建议Thread实例。 public class ThreadTest extedns Thread{} run方法格式&#xff1a; public void run(){} 20.1让线程循…

mysql命令导出表结构和数据

MySQL是一款流行的关系型数据库管理系统&#xff0c;是许多公司和组织使用的首选数据库。在日常使用中&#xff0c;我们经常需要对数据库进行备份和复制&#xff0c;因此必须了解如何导出表结构和表数据。以下是使用MySQL命令导出表结构和表数据的方法。 1. 导出表结构 mysql…

AnalyticDB for PostgreSQL 实时数据仓库上手指南

AnalyticDB for PostgreSQL 实时数据仓库上手指南 2019-04-016601 版权 本文涉及的产品 云原生数据仓库 ADB PostgreSQL&#xff0c;4核16G 50GB 1个月 推荐场景&#xff1a; 构建的企业专属Chatbot 立即试用 简介&#xff1a; AnalyticDB for PostgreSQL 提供企业级数…

iOS合并代码后解决冲突

合并主干和分支代码后有冲突&#xff0c;xcode无法运行&#xff0c;如下图&#xff1a;文件显示不了&#xff0c;项目名也显示不了 解决冲突&#xff1a; 1.选中左边目录栏的项目名。鼠标右键--> Show in Finder 2.选中项目文件 xxxx.xcodeproj。鼠标右键--> 显示包内容…

【python学习】中级篇-绘制图形:turtle库

turtle库是Python的一个绘图库&#xff0c;它提供了一个简单的绘图窗口&#xff0c;可以让你通过控制一个小海龟在屏幕上移动来绘制图形。 以下是一个简单的turtle库用法示例&#xff1a; 在这个示例中&#xff0c;我们首先导入了turtle库&#xff0c;然后创建了一个画布和一个…

【创建一个C++线程需要传入几个参数?】

在C++中,创建一个std::thread对象时,你可以传递多个参数,但至少需要一个参数:线程将要执行的函数。这个函数可以是一个普通函数、一个类的成员函数,或者一个可调用对象(如lambda表达式)。除了要执行的函数之外,你还可以传递任何数量的参数,这些参数将被转发到线程函数…

idea手动导入maven包

当maven仓库中没有包时&#xff0c;我们需要手动导入jar到maven项目中 1.这里的maven设置成你自己安装的maven 2.查看pom.xml文件中maven&#xff0c;以下面为例 <dependency><groupId>com.jdd.pay</groupId><artifactId>mapi-sdk-v3</artifactId&…

零基础小白如何自学 Python 步骤和知识点?

Python是一种流行的编程语言&#xff0c;被广泛用于Web开发、数据分析、机器学习、人工智能等领域。如果你是零基础的Python初学者&#xff0c;不必担心&#xff0c;以下是一些建议&#xff0c;帮助你开始Python编程之旅。 黑马程序员python教程&#xff0c;8天python从入门到…

计网(复习自用)

计算机网络 1.概述 1.1概念 含义 计算机网络&#xff1a;是一个将分散的。具有独立功能的计算机系统&#xff0c;通过通信设备和线路连接起来&#xff0c;由功能完善的软件实现资源共享和信息传递的系统。 简单点说&#xff0c;计算机网络是互联的&#xff0c;自治的计算机集…

js方法(数组,对象,字符串,时间,内置方法等)

目录 1.数组方法 2. 对象方法 3. 字符串方法 4.数学方法 5. 日期和时间方法 6.内置方法 7.json方法 1.数组方法 push()&#xff1a;向数组末尾添加一个或多个元素&#xff0c;并返回新的长度。pop()&#xff1a;删除并返回数组的最后一个元素。unshift()&#xff1a;向数…

浅谈勒索病毒防护

免责声明 文章仅做经验分享用途&#xff0c;利用本文章所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;作者不为此承担任何责任&#xff0c;一旦造成后果请自行承担&#xff01;&#xff01;&#xff01; 目录 免责声明 一&am…

使用Python调用API接口获取小红书笔记详情数据

本文将详细介绍如何使用Python编程语言调用小红书API接口&#xff0c;以获取小红书笔记的详情数据。我们将从以下几个方面展开讨论&#xff1a;1) API接口简介&#xff1b;2) Python环境准备&#xff1b;3) API密钥获取&#xff1b;4) 使用Requests库发送API请求&#xff1b;5)…

Android系统调试工具大全:解密adb、dumpsys、procrank等神器

Android系统调试工具大全&#xff1a;解密adb、dumpsys、procrank等神器 引言 Android开发中&#xff0c;调试是一个非常重要的环节&#xff0c;本文将介绍一些常用的Android系统调试工具&#xff0c;包括adb、logcat、procrank、dumpsys、dmesg、top、free、df、trace、pm、…

【python学习】基础篇-常用模块-hashlib模块:常见的哈希算法,如MD5、SHA1、SHA256等计算

Python中的hashlib模块提供了常见的哈希算法&#xff0c;如MD5、SHA1、SHA256等。 使用hashlib模块可以方便地计算字符串或文件的哈希值&#xff0c;常用于密码加密、数据完整性校验等场景。 以下是hashlib模块中一些常见哈希算法的用法&#xff1a; 在实际应用中&#xff0c;…

vue项目引入element-plus

文章目录 引入框架遇到的问题引入的时候&#xff0c;报错 ...(reading replace)...报错&#xff1a;The template root requires ...eslint报错&#xff1a; 运行 引入框架 使用add引入 遇到的问题 引入的时候&#xff0c;报错 …(reading ‘replace’)… Cannot read prop…

聊聊鼎叔的新书《无测试组织-测试团队的敏捷转型》

这是鼎叔的第八十篇原创文章。行业大牛和刚毕业的小白&#xff0c;都可以进来聊聊。 欢迎关注本公众号《敏捷测试转型》&#xff0c;星标收藏&#xff0c;大量原创思考文章陆续推出。 本书官方购买链接请点击 https://item.jd.com/14105386.html。 为何写作本书 对于“无测试组…

C#中委托和匿名方法

目录 一、委托 1.委托的实例化 2.委托的调用 3.示例 二、匿名方法 为了实现方法的参数化&#xff0c;提出了委托的概念&#xff0c;委托是一种引用方法的类型&#xff0c;即委托是方法的引用&#xff0c;一旦为委托分配了方法&#xff0c;委托将与该方法具有完全相同的行为…

DOM事件的传播机制

DOM事件的传播机制是指当一个事件在DOM树中触发时&#xff0c;它是如何在各个元素之间传播的。DOM事件传播机制分为三个阶段&#xff1a;捕获阶段、目标阶段和冒泡阶段。此外&#xff0c;还有一种常用的技术称为事件委托&#xff0c;它能够简化事件处理程序的绑定和管理。本文将…

入选《数据结构与算法领域内容帮榜》第44名

入选《数据结构与算法领域内容帮榜》第44名