3.1、树的表示方法3.2、树的基本术语 1、结点、结点的度和树的度 结点:包含一个元素及若干指向子树的分支 结点的度:结点所拥有的子树数 树的度:树内各结点度的最大值 叶子结点:度为零的结点,也称为终端结点 分支结点:度不为零的结点,也称为非终端结点 2、孩子和双亲 孩子:一个结点子树的根是该结点的孩子,也称为儿子 双亲:一个结点是其子树根的双亲,也称为父亲 兄弟:同一个双亲的两个结点 3、层次和深度 层次:在一棵树中,根结点的层次为 1,其他结点的层次等于其双亲结点的层次加 1 深度:树中叶子结点的最大层次,也称为高度 堂兄弟:双亲结点在同一层的结点 3.3、二叉树 3.3.1、特殊形态的二叉树 满二叉树:一棵深度为 k 且有 2k-1 个结点的二又树称为满二叉树。 满二叉树的特点: (1) 每一层上的结点数都达到最大值。即对给定的高度,它是具有最多结点数的二叉树。 (2) 满二叉树中不存在度数为 1 的结点,每个分支结点均有两棵高度相同的子树,且树叶都在最下一层 上。
完全二叉树:若一棵二叉树至多只有最下面的两层上结点的度数可以小于 2,并且最下一层上的结点都集 中在该层最左边的若干位置上,则此二叉树称为完全二叉树。 完全二叉树的特点: (1) 满二叉树是完全二叉树,完全二叉树不一定是满二叉树。 (2) 在满二叉树的最下一层上,从最右边开始连续删去若干结点后得到的二叉树仍然是一棵完全二叉树。 (3) 在完全二叉树中,若某个结点没有左孩子,则它一定没有右孩子,即该结点必是叶结点。
3.3.2、二叉树的基本性质 性质 1 二叉树第 i 层上的结点数目最多为 2i-1(i≥1)。 性质 2 深度为 k 的二叉树至多有 2k-1 个结点(k≥1)。 性质 3 在任意-棵二叉树中,若终端结点的个数为 n0,度为 2 的结点数为 n2,则 no=n2+1。 性质 4 具有 n 个结点的完全二叉树的深度为
3.3.3、二叉树的存储表示
3.3.4、遍历二叉树 1.中序遍历的递归算法定义: (1)遍历左子树; (2)访问根结点; (3)遍历右子树。 2.先序遍历的递归算法定义: (1) 访问根结点; (2) 遍历左子树; (3) 遍历右子树。 3.后序遍历得递归算法定义: (1)遍历左子树; (2)遍历右子树; (3)访问根结点。 3.3.5、线索二叉树
3.3.6、二叉查找树 二叉排序树又称二叉查找树。 具有下列性质: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值; (2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值; (3)左、右子树也分别为二叉排序树; namespace 二叉查找树 { class Program { static void Main(string[] args) { BinarySearchTree nums = new BinarySearchTree(); nums.Insert(100); nums.Insert(25); nums.Insert(125); nums.Insert(99); nums.Insert(50); nums.Insert(70); nums.Insert(160); nums.Insert(115); Console.WriteLine("--------中序遍历-----------"); nums.InOrder(nums.root); Console.WriteLine(); Console.WriteLine("--------查找最小值---------"); Console.WriteLine(nums.FindMin()); Console.WriteLine("--------查找最大值---------"); Console.WriteLine(nums.FindMax()); nums.Delete(99); Console.WriteLine("--------删除结点后-------"); nums.InOrder(nums.root); Console.ReadLine(); } } public class Node { public int Data; public Node Left; public Node Right; public void DisplayNode() { Console.Write(Data+" "); } } public class BinarySearchTree { public Node root; public BinarySearchTree() { root = null; } public void Insert(int i) //插入元素 { Node newNode = new Node(); newNode.Data = i; if (root == null) root = newNode; else { Node current = root; Node parent; while (true) { parent = current; if (i < current.Data) { current = current.Left; if (current == null) { parent.Left = newNode; break; } } else { current = current.Right; if (current == null) { parent.Right = newNode; break; } } } } } public void InOrder(Node theRoot) //中序遍历 { if (!(theRoot == null)) { InOrder(theRoot.Left); theRoot.DisplayNode(); InOrder(theRoot.Right); } } public void PreOrder(Node theRoot) //先序遍历 { if (!(theRoot == null)) { theRoot.DisplayNode(); PreOrder(theRoot.Left); PreOrder(theRoot.Right); } } public void PostOrder(Node theRoot) //后序遍历 { if (!(theRoot==null)) { PostOrder(theRoot.Left); PostOrder(theRoot.Right); theRoot.DisplayNode(); } } public int FindMin() //查找最小值 { Node current = root; while (!(current.Left == null)) current = current.Left; return current.Data; } public int FindMax() //查找最大值 { Node current = root; while (!(current.Right == null)) current = current.Right; return current.Data; } public Node Find(int key) //查找特定结点 { Node current = root; while (current.Data != key) { if (key < current.Data) { current = current.Left; } else { current = current.Right; } if (current == null) return null; } return current; } public bool Delete(int key) { Node current = root; Node parent = root; bool isLeftChild = true; //是否为左结点 while (current.Data != key) //判断当前结点是否为左结点 { parent = current; if (key < current.Data) { isLeftChild = true; current = current.Left; } else { isLeftChild = false; current = current.Right; } if ((current == null)) return false; } if ((current.Left == null) & (current.Right == null)) //当前结点的左右结点为空 { if (current == root) root = null; else if (isLeftChild) parent.Left = null; else parent.Right = null; } else if (current.Right == null) //当前结点的右结点为空 { if (current == root) root = current.Left; else if (isLeftChild) parent.Left = current.Left; else parent.Right = current.Left; } else if (current.Left == null) //当前结点的左结点为空 { if (current == root) root = current.Right; else if (isLeftChild) parent.Left = current.Right; else parent.Right = current.Right; } else { Node successor = GetSuccessor(current); if (current == root) root = successor; else if (isLeftChild) parent.Left = successor; else parent.Right = successor; successor.Left = current.Left; } return true; } public Node GetSuccessor(Node delNode) //查找要删除结点的后继结点 { Node successorParent = delNode; Node successor = delNode; Node current = delNode.Right; while (!(current == null)) { successorParent = current; successor = current; current = current.Left; } if (!(successor == delNode.Right)) { successorParent.Left = successor.Right; successor.Right = delNode.Right; } return successor; } } }
3.4、哈夫曼树 在权为 wl,w2,„,wn 的 n 个叶子所构成的所有二叉树中,带权路径长度最小(即代价最小)的二叉树称为最 优二叉树或哈夫曼树(WPL 为最小的二叉树) 。 树的带权路径长度(Weighted Path Length of Tree,简记为 WPL) 结点的权:在一些应用中,赋予树中结点的一个有某种意义的实数。 结点的带权路径长度:结点到树根之间的路径长度与该结点上权的乘积。 树的带权路径长度:定义为树中所有叶结点的带权路径长度之和,通常记为:
其中: n 表示叶子结点的数目 wi 和 li 分别表示叶结点 ki 的权值和根到结点 ki 之间的路径长度。 树的带权路径长度亦称为树的代价。
注意: 1 叶子上的权值均相同时,完全二叉树一定是最优二叉树,否则完全二叉树不一定是最优二叉树。 2 最优二叉树中,权越大的叶子离根越近。 3 最优二叉树的形态不唯一,WPL 最小
哈夫曼编码:从哈夫曼树根结点开始,对左子树分配代码“0”,右子树分配代码“1”,一直到达叶子结 点为止,然后将从树根沿每条路径到达叶子结点的代码排列起来,便得到了哈夫曼编码。 设各字母的使用频度为 {E,M,C,A,D}={1,2,3,3,4}。用频度为权值生成哈夫曼树,并在叶子上标注对应的 字母,树枝分配代码“0”或“1”:
3.5、树
![]()
数据结构与算法(C#)--树和二叉树
最新推荐文章于 2025-05-13 13:07:34 发布