知识大全 二叉树
Posted 知
篇首语:犀渠玉剑良家子,白马金羁侠少年。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 二叉树相关的知识,希望对你有一定的参考价值。
目录 二叉树三种周游(traversal)方式 怎样从顶部开始逐层打印二叉树结点数据 如何判断一棵二叉树是否是平衡二叉树 设计一个算法 找出二叉树上任意两个节点的最近共同父结点 复杂度如果是O(n )则不得分 如何不用递归实现二叉树的前序/后序/中序遍历? 在二叉树中找出和为某一值的所有路径 怎样编写一个程序 把一个有序整数数组放到二叉树中? 判断整数序列是不是二叉搜索树的后序遍历结果 求二叉树的镜像 一棵排序二叉树(即二叉搜索树BST) 令 f=(最大值+最小值)/ 设计一个算法 找出距离f值最近 大于f值的结点 复杂度如果是O(n )则不得分 把二叉搜索树转变成排序的双向链表
首先写一个二叉树的C#实现 这是我们的基石 public class BinNode public int Element; public BinNode Left; public BinNode Right; public BinNode(int element BinNode left BinNode right) this Element = element; this Left = left; this Right = right;
public bool IsLeaf() return this Left == null &# ;&# ; this Right == null;
二叉树三种周游(traversal)方式 )前序周游(preorder) 节点 –> 子节点Left(包括其子树) –> 子节点Right(包括其子树) static void PreOrder(BinNode root) if (root == null) return; //visit current node Console WriteLine(root Element); PreOrder(root Left); PreOrder(root Right);
)后序周游(postorder) 子节点Left(包括其子树) –> 子节点Right(包括其子树) –> 节点 static void PostOrder(BinNode root) if (root == null) return; PostOrder(root Left); PostOrder(root Right); //visit current node Console WriteLine(root Element);
)中序周游(inorder) 子节点Left(包括其子树) –> 节点 –> 子节点Right(包括其子树) static void InOrder(BinNode root) if (root == null) return; InOrder(root Left); //visit current node Console WriteLine(root Element); InOrder(root Right);
我们发现 三种周游的code实现 仅仅是访问当前节点的这条语句所在位置不同而已
怎样从顶部开始逐层打印二叉树结点数据 有 种算法 算法 基于Queue来实现 也就是广度优先搜索(BFS)的思想 static void PrintTree (BinNode root) if (root == null) return; BinNode tmp = null; Queue queue = new Queue(); queue Enqueue(root); while (queue Count > ) tmp = (BinNode)queue Dequeue(); Console WriteLine(tmp Element); if (tmp Left != null) queue Enqueue(tmp Left); if (tmp Right != null) queue Enqueue(tmp Right);
话说 BFS和DFS思想本来是用于图的 但我们不能被传统的思维方式所束缚
算法 基于单链表实现 如果没有Queue给我们用 我们只好使用单链表 把每个节点存在单链表的Data中 实现如下 public class Link public Link Next; public BinNode Data; public Link(Link next BinNode data) this Next = next; this Data = data; 看过了Queue的实现 我们发现永远是先出队 个(队头) 然后入队 个(把出队的Left和Right放到队尾) 对于单链表而言 我们可以先模拟入队——把first的Data所对应的Left和Right 先后插到second的后面 即second Next和second Next Next位置 同时second向前走 或 次 再次到达链表末尾 这取决于Left和Right是否为空 然后我们模拟出队——first前进 步 当first指针走不下去了 那么任务也就结束了 static void PrintTree (BinNode root) if (root == null) return; Link head = new Link(null root); Link first = head; Link second = head; while (first != null) if (first Data Left != null) second Next = new Link(null first Data Left); second = second Next; if (first Data Right != null) second Next = new Link(null first Data Right); second = second Next; Console WriteLine(first Data Element); first = first Next;
如何判断一棵二叉树是否是平衡二叉树 平衡二叉树的定义 如果任意节点的左右子树的深度相差不超过 那这棵树就是平衡二叉树 算法思路 先编写一个计算二叉树深度的函数GetDepth 利用递归实现 然后再递归判断每个节点的左右子树的深度是否相差 static int GetDepth(BinNode root) if (root == null) return ; int leftLength = GetDepth(root Left); int rightLength = GetDepth(root Right); return (leftLength > rightLength leftLength : rightLength) + ;
注意这里的+ 对应于root不为空(算作当前 个深度) static bool IsBalanceTree(BinNode root) if (root == null) return true; int leftLength = GetDepth(root Left); int rightLength = GetDepth(root Right); int distance = leftLength > rightLength leftLength &# ; rightLength : rightLength &# ; leftLength;
if (distance > ) return false; else return IsBalanceTree(root Left) &# ;&# ; IsBalanceTree(root Right);
上述程序的逻辑是 只要当前节点root的Left和Right深度差不超过 就递归判断Left和Right是否也符合条件 直到为Left或Right为null 这意味着它们的深度为 能走到这一步 前面必然都符合条件 所以整个二叉树都符合条件
设计一个算法 找出二叉树上任意两个节点的最近共同父结点 复杂度如果是O(n )则不得分 本题网上有很多算法 都不怎么样 这里提出包氏的两个算法 算法 做一个容器 我们在遍历二叉树寻找节点的同时 把从根到节点的路径扔进去(两个节点就是两个容器) 由于根节点最后一个被扔进去 但我们接下来又需要第一个就能访问到它——后进先出 所以这个容器是一个栈 时间复杂度O(N) 空间复杂度O(N) static bool GetPositionByNode(BinNode root BinNode node ref Stack stack) if (root == null) return false; if (root == node) stack Push(root); return true; if (GetPositionByNode(root Left node ref stack) || GetPositionByNode(root Right node ref stack)) stack Push(root); return true; return false;
然后我们要同时弹出这两个容器的元素 直到它们不相等 那么之前那个相等的元素就是我们要求的父亲节点 static BinNode FindParentNode(BinNode root BinNode node BinNode node ) Stack stack = new Stack(); GetPositionByNode(root node ref stack ); Stack stack = new Stack(); GetPositionByNode(root node ref stack ); BinNode tempNode = null; while (stack Peek() == stack Peek()) tempNode = (BinNode)stack Pop(); stack Pop(); return tempNode;
算法 如果要求o( )的空间复杂度 就是说 只能用一个变量来辅助我们 我们选择一个 位的整数 然后从 开始 从左到右逐层为二叉树的每个元素赋值 root对应 root Left对应 root Right对应 依次类推 而不管实际这个位置上是否有节点 我们发现两个规律 //// //// //// //// 如果要找的是 和 位置上的节点 我们发现 它们的二进制分别是 和 右移 使之与 位数相同 于是 变成了 (也就是 的父亲 ) 这时 和 (也就是 和 位于同样的深度) 我们从左往右找 和 具有 位相同 即 这就是我们要找的 和 的父亲 也就是 和 的最近父亲 由上面观察 得到算法 )将找到的两个节点对应的数字 static bool GetPositionByNode(BinNode root BinNode node ref int pos) if (root == null) return false; if (root == node) return true; int temp = pos; //这么写很别扭 但是能保证只要找到就不再进行下去 pos = temp * ; if (GetPositionByNode(root Left node ref pos)) return true; else //找不到左边找右边 pos = temp * + ; return GetPositionByNode(root Right node ref pos); )它们的二进制表示 从左向右逐一比较 直到一个结束或不再相同 则最大的相同子串 就是我们需要得到的最近父亲所对应的位置K static int FindParentPosition(int larger int smaller) if (larger == smaller) return larger; int left = GetLen(larger) &# ; GetLen(smaller); while (left > ) larger = larger >> ; left&# ;; while (larger != smaller) larger = larger >> ; smaller = smaller >> ; return smaller; static int GetLen(int num) int length = ; while (num != ) num = num >> ; length++; return length; )第 次递归遍历 寻找K所对应的节点 函数GetNodeByPosition的思想是 先算出k在第几层power 观察k的二进制表示 比如说 即 从左向右数第一个位 不算 还剩下 表示向右走 表示向左走 于是从root出发 > > > static BinNode GetNodeByPosition(BinNode root int num) if (num == ) return root; int pow = (int)Math Floor(Math Log(num )); // return return return //第一个位不算 num = << pow; while (pow > ) if ((num &# ; << (pow )) == ) root = root Left; else root = root Right; pow ; return root;
总结上面的 个步骤 static BinNode FindParentNode(BinNode root BinNode node BinNode node ) int pos = ; GetPositionByNode(root node ref pos ); int pos = ; GetPositionByNode(root node ref pos ); int parentposition = ; if (pos >= pos ) parentposition = FindParentPosition(pos pos ); else //pos
parentposition = FindParentPosition(pos pos ); return GetNodeByPosition(root parentposition);
如何不用递归实现二叉树的前序/后序/中序遍历? 算法思想 三种算法的思想都是让root的Left的Left的Left全都入栈 所以第一个while循环的逻辑 都是相同的 下面详细分析第 个while循环 这是一个出栈动作 只要栈不为空 就始终要弹出栈顶元素 由于我们之前入栈的都是Left节点 所以每次在出栈的时候 我们都要考虑Right节点是否存在 因为前序/后序/中序遍历顺序的不同 所以在具体的实现上有略为区别 )前序遍历 这个是最简单的 前序遍历是root >root Left >root Right的顺序 因为在第一个while循环中 每次进栈的都可以认为是一个root 所以我们直接打印 然后root Right和root Left先后进栈 那么出栈的时候 就能确保先左后右的顺序 static void PreOrder(BinNode root) Stack stack = new Stack(); BinNode temp = root; //入栈 while (temp != null) Console WriteLine(temp Element); if (temp Right != null) stack Push(temp Right); temp = temp Left; //出栈 当然也有入栈 while (stack Count > ) temp = (BinNode)stack Pop(); Console WriteLine(temp Element); while (temp != null) if (temp Right != null) stack Push(temp Right); temp = temp Left; //后序遍历比较麻烦 需要记录上一个访问的节点 然后在本次循环中判断当前节点的Right或Left是否为上个节点 当前节点的Right为null表示没有右节点 static void PostOrder(BinNode root) Stack stack = new Stack(); BinNode temp = root; //入栈 while (temp != null) if (temp != null) stack Push(temp); temp = temp Left; //出栈 当然也有入栈 while (stack Count > ) BinNode lastvisit = temp; temp = (BinNode)stack Pop(); if (temp Right == null || temp Right == lastvisit) Console WriteLine(temp Element); else if (temp Left == lastvisit) stack Push(temp); temp = temp Right; stack Push(temp); while (temp != null) if (temp Left != null) stack Push(temp Left); temp = temp Left; //中序遍历 类似于前序遍历 static void InOrder(BinNode root) Stack stack = new Stack(); BinNode temp = root; //入栈 while (temp != null) if (temp != null) stack Push(temp); temp = temp Left; //出栈 当然也有入栈 while (stack Count > ) temp = (BinNode)stack Pop(); Console WriteLine(temp Element); if (temp Right != null) temp = temp Right; stack Push(temp); while (temp != null) if (temp Left != null) stack Push(temp Left); temp = temp Left;
在二叉树中找出和为某一值的所有路径 算法思想 这道题目的苦恼在于 如果用递归 只能打出一条路径来 其它符合条件的路径打不出来 为此 我们需要一个Stack 来保存访问过的节点 即在对该节点的递归前让其进栈 对该节点的递归结束后 再让其出栈——深度优先原则(DFS) 此外 在递归中 如果发现某节点(及其路径)符合条件 如何从头到尾打印是比较头疼的 因为DFS使用的是stack而不是queue 为此我们需要一个临时栈 来辅助打印 static void FindBinNode(BinNode root int sum Stack stack) if (root == null) return; stack Push(root Element); //Leaf if (root IsLeaf()) if (root Element == sum) Stack tempStack = new Stack(); while (stack Count > ) tempStack Push(stack Pop()); while (tempStack Count > ) Console WriteLine(tempStack Peek()); stack Push(tempStack Pop()); Console WriteLine(); if (root Left != null) FindBinNode(root Left sum &# ; root Element stack); if (root Right != null) FindBinNode(root Right sum &# ; root Element stack); stack Pop();
怎样编写一个程序 把一个有序整数数组放到二叉树中? 算法思想 我们该如何构造这棵二叉树呢?当然是越平衡越好 如下所示 //// arr[ ] //// arr[ ] arr[ ] //// arr[ ] arr[ ] arr[ ] 相应编码如下 public static void InsertArrayIntoTree(int[] arr int pos ref BinNode root) root = new BinNode(arr[pos] null null); root Element = arr[pos]; //if Left value less than arr length if (pos * + > arr Length &# ; ) return; else InsertArrayIntoTree(arr pos * + ref root Left); //if Right value less than arr length if (pos * + > arr Length &# ; ) return; else root Right = new BinNode(arr[pos * + ] null null); InsertArrayIntoTree(arr pos * + ref root Right);
判断整数序列是不是二叉搜索树的后序遍历结果 比如 给你一个数组 int a[] = [ ] 则F(a) => false 算法思想 在后续遍历得到的序列中 最后一个元素为树的根结点 从头开始扫描这个序列 比根结点小的元素都应该位于序列的左半部分 从第一个大于跟结点开始到跟结点前面的一个元素为止 所有元素都应该大于跟结点 因为这部分元素对应的是树的右子树 根据这样的划分 把序列划分为左右两部分 我们递归地确认序列的左 右两部分是不是都是二元查找树 由于不能使用动态数组 所以我们每次递归都使用同一个数组arr 通过start和length来模拟 部分 数组 public static bool VerifyArrayOfBST(int[] arr int start int length) if (arr == null || arr Length == || arr Length == ) return false; int root = arr[length + start ]; int i = start; for (; i < length ; i++) if (arr[i] >= root) break; int j = i; for (; j < length ; j++) if (arr[j] < root) return false; bool left = true; if (i > start) left = VerifyArrayOfBST(arr start i &# ; start); bool right = true; if (j > i) right = VerifyArrayOfBST(arr i j &# ; i + ); return left &# ;& right;
求二叉树的镜像 算法 利用上述遍历二叉树的方法(比如说前序遍历) 把访问操作修改为交换左右节点的逻辑 static void PreOrder(ref BinNode root) if (root == null) return; //visit current node BinNode temp = root Left; root Left = root Right; root Right = temp; PreOrder(ref root Left); PreOrder(ref root Right);
算法 使用循环也可以完成相同的功能 static void PreOrder (ref BinNode root) if (root == null) return; Stack stack = new Stack(); stack Push(root); while (stack Count > ) //visit current node BinNode temp = root Left; root Left = root Right; root Right = temp; if (root Left != null) stack Push(root Left); if (root Right != null) stack Push(root Right);
一棵排序二叉树(即二叉搜索树BST) 令 f=(最大值+最小值)/ 设计一个算法 找出距离f值最近 大于f值的结点 复杂度如果是O(n )则不得分 算法思想 最小最大节点分别在最左下与最右下节点 O(N) static BinNode Find(BinNode root) BinNode min = FindMinNode(root); BinNode max = FindMaxNode(root); double find = (double)(min Element + max Element) / ; return FindNode(root find);
static BinNode FindMinNode(BinNode root) BinNode min = root; while (min Left != null) min = min Left; return min; static BinNode FindMaxNode(BinNode root) BinNode max = root; while (max Right != null) max = max Right; return max; 递归寻找BST的节点 O(logN) static BinNode FindNode(BinNode root double mid) //如果小于相等 则从右边找一个最小值 if (root Element <= mid) if (root Right == null) return root; BinNode find = FindNode(root Right mid); //不一定找得到 return find Element < mid root : find; //如果大于 则找到Left else //temp Element > find if (root Left == null) return root; BinNode find = FindNode(root Left mid); //不一定找得到 return find Element < mid root : find;
把二叉搜索树转变成排序的双向链表 如 //// //// //// //// 转变为Link = = = = = = = 算法思想 这个就是中序遍历啦 因为BST的中序遍历就是一个从小到大的访问顺序 局部修改中序遍历算法 于是有如下代码 static void ConvertNodeToLink(BinNode root ref DoubleLink link) if (root == null) return; BinNode temp = root; if (temp Left != null) ConvertNodeToLink(temp Left ref link); //visit current node link Next = new DoubleLink(link null root); link = link Next; if (temp Right != null) ConvertNodeToLink(temp Right ref link);
但是我们发现 这样得到的Link是指向双链表最后一个元素 而我们想要得到的是表头 为此 我们不得不额外进行while循环 将指针向前移动到表头 static DoubleLink ReverseDoubleLink(BinNode root ref DoubleLink link) ConvertNodeToLink(root ref link); DoubleLink temp = link; while (temp Prev != null) temp = temp Prev; return temp; 这么写有点蠢 为什么不直接在递归中就把顺序反转呢?于是有算法
算法 观察算法 的递归方法 访问顺序是Left > Root –> Right 所以我们要把访问顺序修改为Right > Root –> Left 此外 算法的节点访问逻辑 是连接当前节点和新节点 同时指针link向前走 即 = = = = = = = =link 代码如下所示 link Next = new DoubleLink(link null root); link = link Next; 那么 即使我们颠倒了访问顺序 新的Link也只是变为 = = = = = = = =link 为此 我们修改上面的节点访问逻辑——将Next和Prev属性交换 link Prev = new DoubleLink(null link root); link = link Prev; 这样 新的Link就变成这样的顺序了 link= = = = = = = = 算法代码如下所示 static void ConvertNodeToLink (BinNode root ref DoubleLink link) if (root == null) return; BinNode temp = root; if (temp Right != null) ConvertNodeToLink (temp Right ref link); //visit current node link Prev = new DoubleLink(null link root); link = link Prev; if (temp Left != null) ConvertNodeToLink (temp Left ref link);
以下算法属于二叉树的基本概念 未列出 Huffman Tree的生成 编码和反编码 BST的实现 Heap的实现 优先队列
非平衡二叉树如何变成平衡二叉树?
cha138/Article/program/sjjg/201405/30936相关参考
二叉树具有以下重要性质 性质二叉树第i层上的结点数目最多为i(i≥) 证明用数学归纳法证明 归纳基础i=时有i==因为第层上只有一个根结点所以命题成立 归纳假设假设对所有的j(≤j 归纳
二叉树的线索化 线索化和线索化实质 将二叉树变为线索二叉树的过程称为线索化 按某种次序将二叉树线索化的实质是按该次序遍历二叉树在遍历过程中用线索取代空指针 具体过程可 二叉树的中序线索化
链式存储结构 结点的结构 二叉树的每个结点最多有两个孩子用链接方式存储二叉树时每个结点除了存储结点本身的数据外还应设置两个指针域 lchild和rchild分别指向该结点的左孩子和右孩子结点
顺序存储结构 该方法是把二叉树的所有结点按照一定的线性次序存储到一片连续的存储单元中结点在这个序列中的相互位置还能反映出结点 之间的逻辑关系 完全二叉树结点编号 ()编号办法 在一棵n个
二叉树的特殊形态 满二叉树(FullBinaryTree)一棵深度为k且有k个结点的二叉树 特点二叉树的所有分支结点都存在左子树和右子树 特点二叉树的所有叶子结点都在同一层上 完全二叉树(Co
(四)树与二叉树的应用 二叉排序树 定义 二叉排序树(也称二叉查找树)或者是一棵空的二叉树或者是具有下列性质的二叉树 ()若它的左子树不空则左子
树或森林与二叉树之间有一个自然的一一对应关系任何一个森林或一棵树可惟一地对应到一棵二叉树反之任何一棵二叉树也能惟一地对应到一个森林或一棵树.树森林到二叉树的转换()将树转换为二叉树 树中每个结点
最优二叉树概念.树的路径长度 树的路径长度是从树根到树中每一结点的路径长度之和在结点数目相同的二叉树中完全二叉树的路径长度最短.树的带权路径长度(WeightedPathLengthofTree简记
二叉树的定义 二叉树(BinaryTree)是n(n≥)个结点的有限集它或者是空集(n=)或者由一个根结点及两棵互不相交的分别称作这个根的左子树和右子树的二叉树组成 若二叉树为空集则称