湖南网站建设 地址磐石网络哈尔滨网站设计公司哪家更好

web/2025/9/29 13:08:36/文章来源:
湖南网站建设 地址磐石网络,哈尔滨网站设计公司哪家更好,网站备案用户名,企业网站优化的原则【本节目标】 树概念及结构。二叉树概念及结构。二叉树常见OJ题练习。 1、树概念及结构 1.1、树的概念 树是一种非线性的数据结构#xff0c;它是由n#xff08;n0#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树#xf…【本节目标】 树概念及结构。二叉树概念及结构。二叉树常见OJ题练习。 1、树概念及结构 1.1、树的概念 树是一种非线性的数据结构它是由nn0个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树也就是说它是根朝上、而叶朝下的。 有一个特殊的结点称为根结点根节点没有前驱节点。除根节点外其余节点被分为成MM0个互不相交的集合T1、T2、…、Tm其中每个集合Ti1im又是一棵结构与树类似的子树。每颗子树的根节点有且只有一个前驱可以有0个或多个后继。因此树是递归定义的。 概念 没有父节点的节点称为根节点。没有子节点的节点称为叶节点。 子树是不相交的。除了根节点外每个节点有且仅有一个父节点。一棵N个节点的树有N-1条边。 下面来说一下树的常见概念以下图为例 结点的度一个节点含有的子树的个数称为该节点的度。如上图A节点的度为6。 叶(子)结点或终端结点度为0的节点成额为叶节点。如上图B、C、H、I…等节点为叶节点。 非终端结点或分支结点度不为0的节点。如上图D、E、F、G…等节点为分支节点。 双亲结点或父结点若一个节点含有子节点则这个节点称为其子节点的父节点。如上图A是B的父节点。 孩子结点或子结点一个节点含有的子树的根节点称为该节点的子节点。如上图B是A的子节点。 兄弟结点具有相同父节点互称为兄弟节点。如上图B、C是兄弟节点。 树的度一棵树中最大节点的度称为树的度。如上图树的度为6。 结点的层次从根开始定义起根为第一层根的子节点为第2层以此类推。 树的高度或深度树中节点的最大层次。如上图树的高度为4。 结点的祖先从根到该结点所经分支上的所有节点。如上图A是所有节点的祖先。A、E、J是Q的祖先。 子孙以某节点为根的子树中任一节点都称为该节点的子孙。如上图所有节点都是A的子孙。I、J、P、Q是E的子孙。 森林由mm0棵树互不相交的多棵树的集合称为森林。数据结构中的学习并查集本质就是森林。 树一定是森林单森林不一定是树。 1.2、树的表示 树结构相对线性表就比较复杂了要存储表示起来就比较麻烦实际中树有很多种表示方法如双亲表示法孩子表示法孩子兄弟表示法等等。我们这里就简单的了解其中最常用的__孩子兄弟表示法__。 typedef int DataType; struct Node {struct Node* _firstChild1; //第一个孩子节点struct Node* _pNextBrother; //指向其下一个兄弟节点DataType _data; //节点中的数据域 };1.3、树在实际中的运用表示文件系统的目录树结构 2、二叉树概念及结构 为什么需要二叉树呢 首先二叉树有以下特点 二叉树的结构最简单规律性最强。可以证明所有树都能转为唯一对应的二叉树不失一般性。 普通树多叉树若不转化为二叉树则运算很难实现。 二叉树在树结构的应用中起这非常重要的作用因为对二叉树的许多操作算法简单而任何树都可以与二叉树相互转换这样就解决了树的存储结构及其运算中存在的复杂性。 2.1、概念 一棵二叉树是节点的一个有限集合该集合或者为空或者由一个根节点加上两棵称为左子树和右子树的二叉树组成。 二叉树的特点 ​ 1、每个节点最多由两颗子树即二叉树不存在度大于2的节点。 ​ 2、二叉树的子树有左右之分其子树的次序不能颠倒。 2.2、二叉树的性质 性质一在二叉树的第i层上至多有2^(i-1)个结点i1。第i层上至少有1个结点。 性质二深度为k的二叉树至多有2^k - 1个结点k1。深度为k时至少有k个结点。 性质三对任何一棵二叉树如果度为0的叶节点个数为n0度为2的分支节点个数为n2则有n0 n21。 如下图演示 如上图所示度为0的节点个数有8个n0。度为2的节点有7个n2所以n0 n21。 我们看个图 如上图所示度为0的节点有两个n0F、E。度为2的节点有1个n2A。 所以n0 n21。 性质四若规定根节点的层数为1具有n个节点的满二叉树的深度h为hlog2 NN是总结点个数。 2.3、特殊的二叉树 2.3.1、满二叉树 满二叉树一个二叉树如果每一层的节点数都达到最大值则这个二叉树就是满 二叉树。也就是说如果一个二叉树的层数为k且节点总数示2^k-1则它就是满二叉树。 【性质】满二叉树中度为1的节点最多为1个。度为1的个数要么为0要么为1。 所以满二叉树可以使用数组进行存储。 2.3.2、完全二叉树 完全二叉树完全二叉树示效率很高的数据结构完全二叉树是由满二叉树引出来的。对于深度为k的由n个节点的二叉树当且仅当其每一个节点都与深度为k的满二叉树中编号从1至n的节点一一对应时称为完全二叉树。要注意的是__满二叉树是一种特殊的完全二叉树__。 换句话说完全二叉树就是假设树的高度为h。 前h-1层都是满的。最后一层可以全满也可以不满。如果最后一层不满要求最后一层结点从左向右都是连续的。 关于完全二叉树的性质 性质一具有n个结点的完全二叉树的深度为[log2 N] 1。 N代表完全二叉树的结点总数。 [x]称作x的底表示不大于x的最大整数。加入x3.14那[x]3。 例题如下图求完全二叉树的深度 可以看到此完全二叉树的结点总数为12那直接套公式[log2 N] 1log2 N约等于3.x所以[log2 N]的结果为3然后再加1最终结果为4。所以此完全二叉树的深度为4。 性质二探讨双亲节点和子节点的关系。 如果i1则结点i是二叉树的根无双亲如果i1则其双亲是结点[i/2]。 如果双亲节点编号是i那么此双亲结点的左节点编号为2i右节点编号是2i1。 2.4、二叉树的存储结构 二叉树一般可以使用两种结构存储一种顺序结构一种链式结构。链式结构又分二叉链三叉链。 2.4.1、顺序存储 顺序结构存储就是使用__数组来存储__一般使用数组只适合表示完全二叉树包含满二叉树因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储关于堆在后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组在逻辑上是一颗二叉树。 完全二叉树存储 非完全二叉树存储 例题二叉树结点数值采用顺序存储结构如图所示。画出二叉树结构 解题思路画出满二叉树的图按照序号一次填入。 2.4.2、链式存储 二叉树的链式存储结构是指用链表来表示一棵二叉树即用链来指示元素的逻辑关系。通常的方法是链表中的每一个节点由三个域组成数据域和左右指针域左右指针分别用来给出该节点左孩子和右孩子所在的链接点的存储地址。链式结构又分为二叉链和三叉链当前我们学习中一般都是二叉链后面课程学到高阶数据结构如红黑树等会用到三叉链。 //二叉链 struct Node {struct Node* _firstChild1; //指向当前节点的左孩子struct Node* _pNextBrother; //指向当前节点的右孩子DataType _data; //节点中的数据域 };//三叉链 struct BinaryTreeNode {struct BinTreeNode* pParent; //指向当前节点的双亲struct BinTreeNode* pLeft; //指向当前节点的左孩子struct BinTreeNode* pRight; //指向当前节点的右孩子int data; //节点中的数据域 };在n个结点的二叉链表中有n1个空指针域。 3、二叉树的一些操作 首先我们在看待二叉树时应该是这样看待任何一颗二叉树有三个部分 根节点左子树右子树 下面我们将要使用的算法是 ​ 分治算法分而治之把大问题分成类似子问题子问题再分为子问题。知道子问题不在可分割。 3.1、二叉树链式结构的遍历 所谓遍历Traversal是指沿着某条搜索路线依次对树中每个节点均作一次且只做一次访问。访问节点所做的操作依赖于具体的应用问题。遍历是二叉树上最重要的运算之一是二叉树上进行其它运算之基础。 __前序/中序/后序的递归结构遍历__是根据访问节点操作发生位置而命名的。 ​ 1、NLR前序遍历Preorder Traversal称为先序遍历——访问根节点的操作发生在遍历其左右子树之 前。 ​ 2、LNR中序遍历Inorder Traversal——访问根节点的操作发生在遍历其左右子树之中间。 ​ 3、LRN后序遍历Postorder Travedsal——访问根节点的操作发生在遍历其左右子树之后。 由于被访问的节点必是某子树的根所以__N(Node)、L(Left subtree)和R(Right subtree)又可解释为根、根的左子树和根的右子树。__NLR、LNR、LRN分别又称为先根遍历、中根遍、后根遍历。 前序中序后序遍历又叫做深度优先遍历。 下面我们以图示来说明__前序遍历、中序遍历、后序遍历__ 前序遍历先根访问顺序A—B—D NULL NULL—E NULL NULL—C NULL NULL。 先放问A然后访问A的左子树也就是P1部分。P1部分先访问B然后访问B的左子树也就是D部分由于D的左子树和右子树都为NULL。所以B的左子树访问结束。之后再访问B的右子树也就是E部分由于E的左子树和右子树都为NULL。所以B的右子树访问结束。拿到这个时候A的左子树访问完毕接着访问A的右子树也就是P2部分。右C的左子树和右子树都为NULL。所以整个二叉树访问完毕。 中序中根左子树 根 右子树 ​ 访问顺序NULL D NULL—B—NULL E NULL—A—NULL C NULL。 ​ 简化顺序D B E A C。 后序后根左子树 右子树 根 ​ 访问顺序NULL NULL D—NULL NULL E—B —NULL NULL C—A。 ​ 简化顺序D E B C A。 ​ 代码实现 #include stdio.h #include stdlib.htypedef int BTDataType;typedef struct BinaryTreeNode {struct BinaryTreeNode* left;struct BinaryTreeNode* right;char data; }BTNode;//前序 void PrevOrder(BTNode* root) {//判断根节点是否为空为空直接返回if (root NULL){return;}printf(%c , root-data);PrevOrder(root-left);PrevOrder(root-right);}//中序 void InOrder(BTNode* root) {if (root NULL)return;InOrder(root-left);printf(%c , root-data);InOrder(root-right); }//后序 void PostOrder(BTNode* root) {if (root NULL)return;PostOrder(root-left);PostOrder(root-right);printf(%c , root-data); }int main() {BTNode* A (BTNode*)malloc(sizeof(BTNode));A-data A;A-left NULL;A-right NULL;BTNode* B (BTNode*)malloc(sizeof(BTNode));B-data B;B-left NULL;B-right NULL;BTNode* C (BTNode*)malloc(sizeof(BTNode));C-data C;C-left NULL;C-right NULL;BTNode* D (BTNode*)malloc(sizeof(BTNode));D-data D;D-left NULL;D-right NULL;BTNode* E (BTNode*)malloc(sizeof(BTNode));E-data E;E-left NULL;E-right NULL;A-left B;A-right C;B-left D;B-right E;printf(前序);PrevOrder(A);printf(\n);printf(中序);InOrder(A);printf(\n);printf(后序);PostOrder(A);printf(\n);return 0; }输出 时间复杂度O(n) //每个结点只访问一次。空间复杂度O(n) //栈占用的最大辅助空间。 3.2、通过使用遍历统计的方法来计算二叉树节点个数 void TreeSize(BTNode* root, int* psize) {if (root NULL){return;}else{(*psize);}TreeSize(root-left,psize);TreeSize(root-right,psize); }int main() {//计算以A为根节点的二叉树节点个数int Asize 0;TreeSize(A, Asize);printf(以A为根节点的节点个数为%d\n, Asize);//计算以B为根节点的二叉树节点个数int Bsize 0;TreeSize(B, Bsize);printf(以B为根节点的节点个数为%d\n, Bsize); }输出 3.3、通过分治的思路来计算二叉树节点个数 int TreeSize(BTNode* root) {return root NULL ? 0 : TreeSize(root-left) TreeSize(root-right) 1; }printf(以A为根节点的节点个数为%d\n, TreeSize(A)); printf(以B为根节点的节点个数为%d\n, TreeSize(B));输出 分析如下图 3.4、通过遍历统计的方法计算二叉树中叶子节点的个数 void TreeLeafSize(BTNode* root, int* psize) {if (root-left NULL root-right NULL){(*psize);}else{TreeLeafSize(root-left, psize);TreeLeafSize(root-right, psize);} }int main() {int a 0;TreeSize(A, a);printf(%d\n,a); }3.5、通过分治的思路来计算二叉树中叶子节点的个数 int TreeLeafSize(BTNode* root) {if (root NULL)return 0;if (root-left NULL root-right NULL)return 1;return TreeLeafSize(root-left) TreeLeafSize(root-right); }int main() {printf(%d\n, TreeLeafSize(A)); }3.6、复制二叉树递归 核心思路 如果是空树递归结束。否则申请新节点空间复制根节点 递归复制左子树。递归复制右子树。 代码实现 #include stdio.h #include stdlib.htypedef struct BinaryTreeNode {struct BinaryTreeNode* left;struct BinaryTreeNode* right;char data; }BTNode;//暴力创建二叉树 BTNode* CreateTree() {BTNode* A (BTNode*)malloc(sizeof(BTNode));A-data A;A-left NULL;A-right NULL;BTNode* B (BTNode*)malloc(sizeof(BTNode));B-data B;B-left NULL;B-right NULL;BTNode* C (BTNode*)malloc(sizeof(BTNode));C-data C;C-left NULL;C-right NULL;BTNode* D (BTNode*)malloc(sizeof(BTNode));D-data D;D-left NULL;D-right NULL;BTNode* E (BTNode*)malloc(sizeof(BTNode));E-data E;E-left NULL;E-right NULL;A-left B;A-right C;B-left D;B-right E;return A; }//复制二叉树 void CopyTree(BTNode* root,BTNode** copy_root) {if (root NULL){//如果主二叉树为空那就将副二叉树指控也就是说不复制了。*copy_root NULL;return 0;}else{*copy_root (BTNode*)malloc(sizeof(BTNode));(*copy_root)-data root-data;CopyTree(root-left, (*copy_root)-left);CopyTree(root-right, (*copy_root)-right);} }//中序遍历 void InOrder(BTNode* root) {if (root NULL){return;}InOrder(root-left);printf(%c , root-data);InOrder(root-right); }//后序遍历 void PostOrder(BTNode* root) {if (root NULL){return;}PostOrder(root-left);PostOrder(root-right);printf(%c , root-data); }int main() {BTNode* root CreateTree();BTNode* copy_root;CopyTree(root, copy_root);printf(中序遍历);InOrder(copy_root);printf(\n);printf(后序遍历);PostOrder(copy_root);return 0; }3.7、二叉树的深度 核心思想 如果是空树则深度为0。否则递归计算左子树的深度记为m递归计算右子树的深度记为n二叉树的深度则为m与n的较大者加1。 代码展示 int maxDepth(struct TreeNode* root){if (root NULL){return 0;}int leftDepth maxDepth(root-left);int rightDepth maxDepth(root-right);return leftDepth rightDepth ? leftDepth1 : rightDepth1; }3.8、二叉树的销毁 这里直接说结论使用后序遍历进行销毁 核心代码实现 //二叉树的销毁 void DestroyTree(struct TreeNode* root) {if (root NULL){return;}DestroyTree(root-left);DestroyTree(root-right);free(root);root NULL; }4、二叉树层序遍历的实现非递归实现 4.1、利用队列实现层序遍历 上面我们进行二叉树的遍历都是用递归的方法前序中序后序又叫深度优先遍历。 那可不可以使用非递归的方法来遍历二叉树呢可以在上面我们也写了2个案例。 此方法叫做层序遍历广度优先遍历。 这里借助__队列实现先进先出。__ 层序遍历的作用是将二叉树从上到下从左到右依次遍历。如下图遍历的结果是A-B-C-D-E-F-G-H。 这种方法的核心思路就是上一层带下一层。 具体实现方法如下图 1、首先有个队列先把节点A放进去。 2、取出A节点注意重点来了。我们说核心思路就是上一层带下一层。因为A连接下一层的BC节点。所以把A取出来之后先把BC节点放进队列中去。 3、然后取出节点B由于B连接的下层有DE节点。所以在取出B节点之后先把DE节点放进队列中。 4、然后将C结点取出由于C左右子树为FG结点。所以在取出C结点后在把FG进栈如下图 就这样以此类推实现效果。 这里简化代码量使用一个简单的二叉树如下和上面的原理一样就是少创建几个二叉树结点。 4.2、代码全放在一个原文件中 代码实现这里的队列使用前面所学写的队列。并且所有代码都在一个源文件中 #include stdio.h #include stdlib.h #include assert.h #include stdbool.h//typedef struct BinaryTreeNode* QDataType;typedef struct BinaryTreeNode {struct BinaryTreeNode* left;struct BinaryTreeNode* right;char data; }BTNode;typedef BTNode* QDataType;typedef struct QueueNode {struct QueueNode* next;QDataType data; }QNode;typedef struct Queue {QNode* head;QNode* tail; }Queue;void QueueInit(Queue* pq); void QueueDestroy(Queue* pq); void QueuePush(Queue* pq, QDataType x); void QueuePop(Queue* pq); //在对头删除数据 QDataType QueueFront(Queue* pq); //取对头的数据 QDataType QueueBack(Queue* pq); //取对尾的数据 int QueueSize(Queue* pq); //计算队列中数据个数 bool QueueEmpty(Queue* pq);void QueueInit(Queue* pq) {assert(pq);pq-head NULL;pq-tail NULL; }QNode* BuyQueueNode(QDataType x) {QNode* newnode (QNode*)malloc(sizeof(QNode));if (newnode NULL){printf(malloc fail\n);exit(-1);}newnode-data x;newnode-next NULL;return newnode; }void QueueDestroy(Queue* pq) {assert(pq);QNode* cur pq-head;while (cur ! NULL){QNode* next cur-next;free(cur);cur next;}pq-head pq-tail NULL; }void QueuePush(Queue* pq, QDataType x) //插入数据其实就是尾插 {assert(pq);QNode* newnode BuyQueueNode(x);if (pq-head NULL){pq-head pq-tail newnode;}else{pq-tail-next newnode;pq-tail newnode;} }void QueuePop(Queue* pq) //在对头删除数据 {assert(pq);//防止pq-head NULL而导致程序崩溃。assert(!QueueEmpty(pq));QNode* next pq-head-next;free(pq-head);pq-head next;if (pq-head NULL){pq-tail NULL;} }QDataType QueueFront(Queue* pq) //取对头的数据 {assert(pq);assert(!QueueEmpty(pq));return pq-head-data; }QDataType QueueBack(Queue* pq) //取对尾的数据 {assert(pq);assert(!QueueEmpty(pq));return pq-tail-data; } int QueueSize(Queue* pq) //计算队列中有多少的数据 {int count 0;QNode* cur pq-head;while (cur ! NULL){count;cur cur-next;}return count; }bool QueueEmpty(Queue* pq) {assert(pq);return pq-head NULL; }void LevelOrder(BTNode* root) {Queue qq;QueueInit(qq);if (root){QueuePush(qq, root);}while (!QueueEmpty(qq)){BTNode* front QueueFront(qq);QueuePop(qq);printf(%c , front-data);if (front-left){QueuePush(qq, front-left);}if (front-right){QueuePush(qq, front-right);}}printf(\n);QueueDestroy(qq); }int main() {BTNode* A (BTNode*)malloc(sizeof(BTNode));A-data A;A-left NULL;A-right NULL;BTNode* B (BTNode*)malloc(sizeof(BTNode));B-data B;B-left NULL;B-right NULL;BTNode* C (BTNode*)malloc(sizeof(BTNode));C-data C;C-left NULL;C-right NULL;BTNode* D (BTNode*)malloc(sizeof(BTNode));D-data D;D-left NULL;D-right NULL;BTNode* E (BTNode*)malloc(sizeof(BTNode));E-data E;E-left NULL;E-right NULL;A-left B;A-right C;B-left D;B-right E;LevelOrder(A); }4.3、代码分布放 queuqe.h #pragma once#include stdio.h #include stdlib.h #include assert.h #include stdbool.htypedef struct BinaryTreeNode {struct BinaryTreeNode* left;struct BinaryTreeNode* right;char data; }BTNode;typedef BTNode* QDataType;typedef struct QueueNode {QDataType data;struct QueueNode* Next; }QNode;typedef struct Queue {struct QueueNode* head;struct QueueNode* tail; }Queue;//队列初始化 void QueueInit(Queue* pq);//销毁 void QueueDestroy(Queue* pq);//扩容 QNode* BuyQueueNode(QDataType x);//对尾插入数据 void QueuePush(Queue* pq, QDataType x);//队头删除数据 void QueuePop(Queue* pq);//取队尾数据 QDataType QueueBack(Queue* pq);//取对头数据 QDataType QueueFront(Queue* pq);//统计队列元素个数 int QueueSize(Queue* pq);//判断队列是否为空 bool QueueEmpty(Queue* pq);queue.c #include queue.h//队列初始化 void QueueInit(Queue* pq) {assert(pq);pq-head NULL;pq-tail NULL; }//销毁 void QueueDestroy(Queue* pq) {assert(pq);QNode* cur pq-head;while (cur){QNode* next cur-Next;free(cur);cur next;}pq-head pq-tail NULL; }//扩容 QNode* BuyQueueNode(QDataType x) {QNode* newnode (QNode*)malloc(sizeof(QNode));if (newnode NULL){printf(malloc fail\n);exit(-1);}newnode-data x;newnode-Next NULL;return newnode; }//对尾插入数据 void QueuePush(Queue* pq, QDataType x) {QNode* newnode BuyQueueNode(x);if (pq-head NULL){pq-head pq-tail newnode;}else{pq-tail-Next newnode;pq-tail newnode;} }//队头删除数据 void QueuePop(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));QNode* next pq-head-Next;free(pq-head);pq-head next;if (pq-head NULL){pq-tail NULL;} }//取队尾数据 QDataType QueueBack(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));return pq-tail-data; }//取对头数据 QDataType QueueFront(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));return pq-head-data; }//统计队列元素个数 int QueueSize(Queue* pq) {assert(pq);assert(!QueueEmpty(pq));int count 0;QNode* cur pq-head;while (cur){count;cur cur-Next;}return count; }//判断队列是否为空 bool QueueEmpty(Queue* pq) {assert(pq);return pq-head NULL; }test.c #include queue.hvoid LevelOrder(BTNode* root) {Queue qq;QueueInit(qq);if (root){QueuePush(qq, root);}while (!QueueEmpty(qq)){BTNode* front QueueFront(qq);QueuePop(qq);printf(%c , front-data);if (front-left){QueuePush(qq, front-left);}if (front-right){QueuePush(qq, front-right);}}printf(\n);QueueDestroy(qq); }int main() {BTNode* A (BTNode*)malloc(sizeof(BTNode));A-data A;A-left NULL;A-right NULL;BTNode* B (BTNode*)malloc(sizeof(BTNode));B-data B;B-left NULL;B-right NULL;BTNode* C (BTNode*)malloc(sizeof(BTNode));C-data C;C-left NULL;C-right NULL;BTNode* D (BTNode*)malloc(sizeof(BTNode));D-data D;D-left NULL;D-right NULL;BTNode* E (BTNode*)malloc(sizeof(BTNode));E-data E;E-left NULL;E-right NULL;A-left B;A-right C;B-left D;B-right E;LevelOrder(A);return 0; }5、二叉树的建立 按先序遍历序列建立二叉树的二叉链表。 例已知先序序列为ABCDEGF。 核心思想 从键盘输入二叉树的结点信息建立二叉树的存储结构。在建立二叉树的过程中按照二叉树先序方式建立。 如果单单给出一个先序也许会有多种接表结构就比如ABCDEGF。会有下面两种不仅限于这两种 那我们到底想要建立那种结构呢换句话说如果我想建立第一种二叉树呢其实也很简单我们将结点左右子树的NULL的地方在表示出来就行了。这里就用#标识NULL吧。那就需要按照下列顺序读入字符ABC##DE#G##F###。 我们在建立二叉树之后在输出中序和后序的结果来验证。 中序C B E G D F A。 后序C G E F D B A。 知道了实现思想下面来看看代码实现 #define _CRT_SECURE_NO_WARNINGS #include stdio.h #include stdlib.htypedef struct BinaryTreeNode {struct BinaryTreeNode* left;struct BinaryTreeNode* right;char data; }BTNode;//创建二叉树 void CreateTree(BTNode** root) {char ch;scanf(%c, ch);if (ch #){*root NULL;}else{*root (BTNode*)malloc(sizeof(BTNode));if (*root NULL){printf(malloc fail\n);exit(-1);}(*root)-data ch;//那这里对应的也需要传地址。CreateTree((*root)-left);CreateTree((*root)-right);} }//中序遍历 void InOrder(BTNode* root) {if (root NULL){return;}InOrder(root-left);printf(%c , root-data);InOrder(root-right); }//后序遍历 void PostOrder(BTNode* root) {if (root NULL){return;}PostOrder(root-left);PostOrder(root-right);printf(%c , root-data); }int main() {BTNode* T;CreateTree(T); //注意这里需要传递结构体指针的地址所以是个二级指针。printf(中序遍历);InOrder(T);printf(\n);printf(后序遍历);PostOrder(T);return 0; }输出 6、线索二叉树 为什么要研究线索二叉树 当用二叉链表作为二叉树的存储结构时可以很方便的找到某个结点的左右孩子但一般情况下无法直接找到该节点在某种遍历序列中的前驱和后继结点。 那如何寻找特定遍历序列中二叉树结点和前驱和后继 解决方法 通过遍历寻找-------费时间。每个结点再增设前驱、后继指针域------增加了存储负担。利用二叉链表中的空指针域本章研究。 【结论】具有n个结点的二叉链表中有n1个指针域为空。 结论剖析具有n个结点的二叉链表中一共有2n个指针域因为n个结点中有n-1个孩子即2n个指针域中有n-1个用来指示结点的左右孩子其余n1个指针域为空。 利用二叉链表中的空指针域 ​ 如果某个结点的左孩子为空则将空的左孩子指针域改为__指向其前驱__如果某结点的右孩子为空则将空的 右孩子指针域改为__指向其后继。__ 这种__改变指向的指针称为“线索”。__ 那加上线索的二叉树称为__线索二叉树Threaded Binary Tree。__ 对二叉树按某种遍历次序使其变为线索二叉树的过程叫__线索化。__ 那如何实现线索化呢如下二叉树其中序遍历CBEGDFA。 注意在强调一遍此二叉树的中序遍历为__C B E G D F A。__下面我们要根据此中序遍历进行线索化。 如下图链接结构 但是要注意不是所有的二叉树线索化都是看中序遍历的顺序。而是要求什么样的遍历就按照什么样的遍历来。 为了区分lrchid和rchild指针到底是指向孩子指针还是指向前驱或者后继的指针对二叉链表每个结点增设两个标志域ltag和rtag并约定 ltag 0lchild指向该结点的左孩子。ltag 1lchild指向该结点的前驱。rtag 0rchild指向该结点的右孩子。rtag 1rchild指向该结点的后继。 这样二叉树结点的结构为 结构实现如下 typedef struct BinaryTreeNode {struct BinaryTreeNode* left;struct BinaryTreeNode* right;int ltag;int rtag;char data; }BTNode;下面我们再来看个二叉树要求先序线索二叉树。 先序序列A B C D E。 那线索化的结果就如下 练习 画出以下二叉树对应的中序线索二叉树。 该二叉树中序遍历结果为H D I B E A F C G。 可以看到H没有前驱G没有后继。那H的左子树和G的右子树结点就置空吗 可以置空。但我们还可以利用起来。 为了避免悬空态增设一个头结点。这个头结点顾名思义就在根节点A的头上。 增设一个头结点 头结点中的ltag0lchild指向根节点。 头结点中的rtag1rchild指向遍历序列中最后一个结点。 然后再将上图二叉树的H、G结点置空的域都指向结点A。 这样以来 遍历序列中第一个结点的lchild域和最后一个结点的rchild域都指向头结点。 如下图 7、搜索二叉树 实际上我们单纯的学习二叉树没有太多的用处。学习二叉树主要是用于搜索二叉树的。如下图 任何一棵树左子树都比根要小右子树都比根要大。 搜索中查找一个数最多查找高度次。 时间复杂度O(N)。 8、树和森林 首先我们先来回顾一下什么是树什么是森林。 树 树是nn0个结点的有限集。若n0称为空树。若n0 有且仅有一个特定的称为根root的结点。其余结点可分为mm0个互不相交的有限集T1T2T3…Tm。 __森林__是m(m0)棵互不相交的树的集合。 8.1、树的存储结构 8.1.1、双亲表示法 实现定义结构数组存放树的的结点每个结点含两个域。 数据域存放结点本身信息。 双亲域指示本结点的双亲结点在数组中的位置。 这样听起来有点抽象我们来说个示例给如下数组写出树的结构。 那根据上面所描述的规则我们就可以写出此树的结构了如下 特点找双亲容易找孩子难。 C语言的类型描述 typedef struct PTNode {Type data;int parent; //双亲位置域 }PTNode;树的结构 #define MAX_TREE_SIZE 100 typedef struct {PTNode node[MAX_TREE_SIZE];int r,n; //根节点的位置和结点个数。 }PTree;8.1.2、孩子链表 把每个结点的孩子结点排列起来看成是一个线性表用单链表存储则n个结点有n个孩子链表叶子结点的孩子链表为空。而n个头指针又组成一个线性表用顺序表含n个元素的结构数组存储。 听起来依然抽象我们来看示例 孩子结点结构 typedef struct CTNode {int child; //用来存放单链表结点中child结点的下标值struct CTNode* Next; //指向下一个结点 }*ChildPtr;双亲结点结构 typedef struct {Type data; //用来存放结点的值ChildPtr firstchild; //用来存放第一个孩子结点的指针。 }CTBox;树结构 typedef struct {CTBox nodes[MAX_TREE_SIZE];int n,t; //结点树和根节点位置。 }CTree;特点找孩子容易找双亲难。 8.1.3、孩子兄弟表示法 孩子兄弟表示法又名二叉树表示法或叉链表表示法。 实现用二叉链表作树的存储结构链表中每个结点的两个指针域分别指向其第一个孩子结点和下一个兄弟结点。 结构描述 typedef struct CSNode {Type data;struct CSNode *firstchild,*nextbother; //firstchild指向其第一个孩子结点,*nextbother指向下一个兄弟结点 }CSNode,*CSTree;下面来看个示例 上面补充B的兄弟结点有两个AC。但为什么B的右指针域不指向A而指向C呢那是因为我们强调是找__下一个兄弟结点__A是B的上一个兄弟节点C才是B结点的下一个兄弟节点所以B的右指针域指向结点C。 现在如果想找到结点C的路径是这样的根据根节点R的firstchild指针域找到结点A然后根据A结点的nextbother指针域找到B最后在根据B结点的nextbother指针域找到C即可。 8.2、树与二叉树的转换 将树转化为二叉树进行处理利用二叉树的算法来实现对树的操作。 那如何操作呢其实可以发现一个对应关系 由于树和二叉树都可以用二叉链表做存储结构则以二叉链表作媒介可以导出树与二叉树之间的一个对应关系。 那是如何对应的呢如下 【说明一下这里树的存储结构采用孩子兄弟法。】 下面来详细说明树和二叉树的转换。 8.2.1、树转换为二叉树 加线在树的原始结构中兄弟结点直接每有联系但转为二叉树前需要将兄弟结点之间加线。 抹线对每个结点除了其左孩子外去除其与其余孩子之间的关系。 如下图A有三个孩子结点B C E现在只将A和其左子树也就是B链接不和C E链接了。又因为在第一步中兄弟结点之间加线了所以B又和C链接了。所以树转换为二叉树了。 旋转以树的根节点为轴心将整数顺时针转45°。 总结为一句口诀兄弟相连留长子。 练习将树转换为二叉树 1兄弟结点之间连线 2除了其左孩子外去除其与其余孩子之间的关系 3以树的根节点为轴心将整数顺时针转45° 这样就完成了树转换为二叉树的过程。 8.2.2、二叉树转换为树 核心步骤 加线若p结点是双亲结点的左孩子则将p的右孩子右孩子的右孩子…沿分支找到的所有右孩子都与p的双亲用线连起来。抹线抹掉原二叉树中双亲与右孩子之间的连线。这里有个简单的规律上一步加多少条线那这一步就会去掉多少条线。调整将结点按层次排序形成树结构。 这个过程就是上面树转为二叉树的逆操作。 口诀左孩右右连双亲去掉原来右孩线。 练习将二叉树转换为树。 1加线 2抹线抹掉原二叉树中双亲与右孩子之间的连线。上一步加了5条线那这一步需要去除5条线 3调整同一层次的结点给调整到同一行。 这样就完成了二叉树转换为树的过程。 8.3、森林与二叉树的转换 8.3.1、森林转换为二叉树 核心步骤 将各棵树分别转换成二叉树。将每棵树的根节点用线相连。以第一颗树根结点为二叉树的根再以根节点为轴心顺时针旋转构成二叉树型结构。 口诀树变二叉根相连。 练习将森林转换为二叉树 1将各棵树分别转换成二叉树。这里不在具体介绍树转换为二叉树的过程了 2将每棵树的根节点用线相连。 3旋转 8.3.2、二叉树转换为森林 核心步骤 抹线将二叉树中根节点与其右孩子连线及沿右分支搜索到的所有右孩子间连线全部抹掉使之变成孤立的二叉树。还原将孤立的二叉树还原成树。 口诀去掉全部右孩线孤立二叉再还原。 练习将二叉树转换为森林 1去掉全部右孩线。 3还原将每个二叉树变为树这里不在具体介绍树二叉转换为树的过程了 8.4、树和森林的遍历 8.4.1、树的遍历 前面学习到二叉树有四种遍历方式先序、中序、后序层序遍历。 而树的遍历有三种方式没有中序遍历。 1、先根次序遍历 ​ 若树不为空则先访问根节点然后依次先根遍历各棵子树。 2、后跟次序遍历 ​ 若树不为空则先依次后根遍历各棵子树然后访问根节点。 3、按层次遍历 ​ 若树不为空则自上而下自左至右访问树中每个结点。 下面给出一个树如下我们来写出先根遍历、后跟遍历、层次遍历的顺序。 先根遍历A B C D E。 后跟遍历B D C E A 。 层序遍历A B C E D。 8.4.2、森林的遍历 将森林看作由三部分构成 森林中第一颗树的根节点。森林中第一颗树的子树森林。森林中其它树构成的森林。 森林的遍历方式也有三种根据访问森林中第一部分的顺序而区分 先序遍历先访问第一部分。中序遍历先访问第一棵树的子树森林再访问第一部分最后访问其它树构成的森林。后序遍历最后访问第一部分。 先序遍历 若森林不空则 访问森林中第一棵树的根节点。先序遍历森林中第一颗树的子树森林。先序遍历森林中除第一棵树之外其余树构成的森林。 即依次从左至右对森林中的每一棵树进行先根遍历。 中序遍历 若森林不空则 中序遍历森林中第一棵树的子树森林。访问森林中第一棵树的根节点。中序遍历森林中除第一棵树之外其余树构成的森林。 即依次从左至右对森林中的每一棵树进行后根遍历。 练习给一个森林如下图进行森林的遍历 先序遍历的结果A B C D E F G H I J。 先序遍历的过程分析 1首先分为三部分 2遍历第一部分得到A结点。再访问第二部分第二部分又是个森林那B C D 结点又是森林中的每个树那就按照树的遍历方法来B是B这个树的根结点遍历B树的根节点那就得到了B结点。那同理得到C结点D结点。至此第二部分遍历完毕。 3最后再访问第三部分第三部分又可以分为三部分如下 然后访问第一部分得到E结点。再访问第二部分F是个子树森林遍历此子树森林得到此子树森林的根节点F。最后再访问第三部分。然后还需要再分为三部分…这里不在细分直接写结果了最后得到了G H I J结点。 所以最终得到此森林的__先序遍历A B C D E F G H I J。__ 练习基于上面问题写出森林的中序遍历 访问顺序先访问第二部分在访问第一部分最后访问第三部分。 中序遍历结果B C D A F E H J I G。

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

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

相关文章

西安模板网站服务商更换模板对网站seo的影响

先说一下自己的情况,本科生,22年通过校招进入南京某软件公司,干了接近2年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了2年的功能测试&…

建立网站需要备案吗上海网站建设公司招聘

背景 分析Fabric网络的启动日志可以帮我们理解搭建区块链网络的过程。本文对输出日志进行详细的分析。 启动网络与创建通道 # verbose 可以输出更详细的日志。 ./network.sh up createChannel -verbose# 使用本机安装的docker and docker-compose Using docker and docker-c…

asp连接数据库做登录网站完整下载免费发布信息的网站平台

在使用Linq的时候经常出现在了这样的一个错误 Row not found or changed. 找不到行或行已更改 解决方案。 首要的解决方案是,检查数据库字段定义,是否有不一致的地方,尤其是 该字段是否可空 Nullable 第一个简单的方案就是 打开dbml (linq)文…

大学生网站建设例题答案寻找锦州网站建设

一、SpringBoot启动配置原理简述 本内容直接查看分析SpringBoot启动配置原理,传送门: 二、SpringBoot启动过程干预 Spring Boot启动过程中我们可以实现以下干预工作: 修改Spring Boot默认的配置属性。使用ConfigurationProperties和Enable…

重庆手机版建站系统哪家好1688会提供网站建设

Logo设计在设计的过程中要考虑很多问题,但是如果时间周期比较短,又要求快速出方案的时候,可以走一些捷径。 在设计logo之前要去了解公司的主营业务、公司规模、公司的名字、公司的主要产品针对的用户群体、甲方的个人偏好、公司原有VIS...这些…

怎么在网站标题做logo360网址导航主页

js 使用多态替换条件语句用多态替换条件语句是一种众所周知的重构模式。 如果您不熟悉该模式,可以在此处查看 。 但是,一旦类中有多个条件检查所基于的字段,该基本解决方案便会开始崩溃。 我们将研究一些有关如何使用这些可能性的想法。 有很…

厦门优秀的网站设计做业务查牙医诊所一般用什么网站

企业信息化规划如何落地? 规划做好了,蓝图也画好了,人手一块大饼也已经揣好了,那么该怎么落地呢,这才是最关键的。 我将企业信息化规划落地分为4个周期,以最典型的制造行业为例,以简道云这个企…

广元园区建设投资有限公司网站邵阳网站制作建设

下载 https://github.com/AntSwordProject/antSword 一句话木马 hack.php脚本 <?php eval($_POST[attack]);?> 安装 1、安装完成后启动 2、初始化&#xff0c;选择有源码的目录 3、连接

域名查询站长工具淘宝主图制作

词法分析程序的功能其实就是对所输入的字段进行词法分析任务&#xff0c;对用户的源程序从左到右进行扫描&#xff0c;按照语言的词法规则识别各类单词&#xff0c;并产生相应单词的属性字&#xff0c;并随之显现出来&#xff0c;成为词法分析程序的功能 转载于:https://www.cn…

dede分类信息网站东莞网站优化是什么

Java中如何解决ClassNotFoundException异常&#xff1f; 大家好&#xff0c;我是免费搭建查券返利机器人省钱赚佣金就用微赚淘客系统3.0的小编&#xff0c;也是冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在Java编程中&#xff0c;ClassNotFoundException异常…

电子商务与网站建设结业论文百度在线咨询

俗话说&#xff0c;“酒香不怕巷子深”;俗话又说&#xff0c;“酒香也怕巷子深” 。再后来&#xff0c;俗话还说&#xff0c;管他酒香不香巷子深不深&#xff0c;只要找个算盘技巧神乎其神的账房先生&#xff0c;即数据科学家&#xff0c;酒就可以大卖了。这叫做用数据驱动用户…

石家庄那有建网站wordpress文章更新插件

目录 实验拓扑&#xff1a; 实验要求&#xff1a; 实验思路&#xff1a; 实验步骤&#xff1a; 1.配置设备接口IP 2.通过配置缺省路由让公网互通 3.配置ppp 1.R1和R5间的ppp的PAP认证&#xff1b; 2.R2与R5之间的ppp的CHAP认证; 3. R3与R5之间的HDLC封装; 4.构建R1、…

仁怀网站建设滁州做网站

概述&#xff1a; 这个仓库旨在通过一个单一的命令&#xff0c;有效地将新鲜且定制化的知识注入到大型语言模型中&#xff0c;以辅助开发人员的工作。 支持的模型&#xff1a;○ GPT-J (6B)○ LLaMA (7B/13B)○ BLOOM (7.1B)○ Falcon (7B)○ Baichuan (7B/13B)○ InternLM (7…

单网页网站 企业如何有效的推广网站

Ubuntu修改自启动软件或程序 在编译程序的时候发现速度很慢&#xff0c;查找发现自启动选项太多&#xff0c;cpu占用率越来越高&#xff0c;现在进行查询并修改权限。 1、查找当前进程 top2、选择相对于的服务查找是否自启动设置 systemctl list-unit-files该指令用以下显示…

如何引用404做网站wordpress好看博客主题

你好&#xff0c;我在我的(新手)Drupal站点有一个ajax调用这个问题。我试图从表单字段保存一些数据&#xff0c;通过jQuery发布到我的Drupal模块中的函数。这里是我的代码&#xff1a;在drupal中使用Ajax时要保持一个404// in drupalfunction mymodule_menu() {$items array()…

建设网站的页面设计分类信息网站建设多少钱

文章目录 一、冒泡排序二、快速排序三、选择排序四、插入排序五、计算排序六、归并排序七、希尔排序八、堆排序九、桶排序十、基数排序 一、冒泡排序 冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就…

石家庄做网站价格建网站成本

在Debug程序时&#xff0c;面对一些大集合&#xff0c;之前是这样查看的&#xff0c;如下图&#xff0c;这样看起来不直观&#xff0c;集合中的数据只能一个一个实体查看&#xff1a;VS2022预览版带来一个新功能&#xff0c;集合表格可视化&#xff0c;比如下面这样一段代码&am…

个人婚礼网站模板做电影网站赚钱

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

wordpress多媒体占用id南山网站seo

个人分析&#xff1a;master-slave优缺点1.物理服务器增加&#xff0c;负荷增加2.主从只负责各自的写和读&#xff0c;极大程度的缓解X锁和S锁争用3.从库可配置myisam引擎&#xff0c;提升查询性能以及节约系统开销4.丛库通过主库发送来的binlog恢复数据&#xff0c;但是&#…

在线网站生成器app开发上线流程

最近几周一直在做DotNET WinForm开发&#xff0c;陆陆续续有些收获&#xff0c;希望能够有空好好整理整理。记下来以免以后又忘了。:-) 一、最简单的线程使用方法 新建一个C# Windows应用程序项目&#xff0c;在最前面的引用代码那增加一行using System.Threading;在界面上扔个…