树和二叉树
2016-04-23 08:52
417 查看
树的定义和基本术语 tree
树的结点包含一个数据元素以及若干个指向其子树的分支。结点拥有的子树数称为结点的度(degree).
度为0的结点称为叶子(leaf)或终端结点。度不为0的结点称为非终端结点或分支结点。
除根节点以外,分支结点也称为内部结点。
树的度是树内各结点的度的最大值。
结点的子树的根称为该结点的孩纸,相应的,该结点称为孩子的双亲(parent).
树中结点的最大层次称为树的深度(depth)或者高度。
如果将树中结点的各子树看成从左至右是有次序的(即不能互换),则称该树为有序树,否则称为无序树。
森林(forest)是多颗互不相交的树的集合。
二叉树 binary tree
性质
在二叉树的第i层上至多有2的(i-1)次方个结点深度为k的二叉树至多有2的k次方-1个结点。
对任何一颗二叉树,如果其终端结点个数为n0,度为2的结点个数为n2,则n0=n2+1;(推导:n=n0+n1+n2,n=1+2n2+n1)
具有n个结点的完全二叉树的深度为[log2-n]+1
满二叉树 一颗深度为k且有2的k次方-1个结点的二叉树称为满二叉树。
完全二叉树
可以对满二叉树的结点进行编号,约定编号从根节点起,自上而下,自左至右。 由此可以引出完全二叉树的定义:深度为k的,有n个结点的二叉树,当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称之为完全二叉树。
二叉树的存储结构
顺序存储结构用一组地址连续的存储单元依次自上而下,自左至右存储完全二叉树上的结点元素,即将完全二叉树上编号为i的结点元素存储在如上定义的一维数组中下标为i-1的分量中。 对于一般二叉树。则应将其每个结点与完全二叉树上的结点相对照,存储在一维数组的相应分量中,0表示不存在此结点。
链式存储结构
二叉链表(数据域,左,右指针域)或者三叉链表(多一个指向双亲结点的链域)。
遍历二叉树
构造二叉树function BinaryNode(value,leftChild,rightChild){ this.value=value; this.lChild=leftChild; this.rChild=rightChild; } var binaryTree = new BinaryNode("-"); binaryTree.leftChild = new BinaryNode("10"); binaryTree.rightChild = new BinaryNode("7"); function visit(binaryNode){ if (binaryNode.value) { console.log(binaryNode.value); } }
递归先序遍历
preOrderVisit(binaryTree); function preOrderVisit(binaryTree){ if (binaryTree) { visit(binaryTree); preOrderVisit(binaryTree.leftChild); preOrderVisit(binaryTree.rightChild); } }
非递归中序遍历
function InorderVisit(binaryTree){ var stack = []; stack.push(binaryTree); while (stack.length) { while (stack[stack.length-1]) { stack.push(stack[stack.length-1].leftChild); } stack.pop(); if (stack.length) { var tmp = stack.pop(); visit(tmp); stack.push(tmp.rightChild); } } }
function InorderVisit(binaryTree){ var stack = [], tmp = binaryTree; while (tmp || stack.length) { if (tmp) { stack.push(tmp);tmp = tmp.leftChild; }else{ tmp = stack.pop(); visit(tmp); tmp = tmp.rightChild; } } }
制造二叉树
//空格用来退出createBiTree var valArr = [0,1,2,"","","",3,4,"",5,"","",6,"","",""]; function createBiTree(){ //按照先序遍历制造二叉树 while (valArr.length) { var binaryNode; var tmp = valArr.shift(); if (tmp !== "") { binaryNode = new BinaryNode(tmp); binaryNode.leftChild = createBiTree(binaryNode.leftChild); binaryNode.rightChild = createBiTree(binaryNode.rightChild); }else { return null; } return binaryNode; } } var test = createBiTree();
线索二叉树 thread binary tree
从上节的讨论可知,遍历二叉树是以一定规则将二叉树中结点排列成一个线性序列。这实质上是一个非线性结构进行线性化操作,使每个结点(除第一个与最后一个)在这些线性序列中有且仅有一个直接前驱和直接后继。 但是,当以二叉链表为结构存储二叉树时,只能找到结点的左右孩子结点,而不能直接得到任一结点的直接前驱和后继。解决方案 原理:在有n个结点二叉树中必然会有n+1个空链域,利用这n+1个空链域存储前继后继信息。 故可以在左右孩子指针上新增一个布尔值标识,当左链域指向左孩子结点时为0,当指向前区时为1。 以这种结点结构构成的二叉链表作为二叉树的存储结构,叫做线索链表。
中序遍历双向线索表
function BinaryThrNode(obj) { this.val = obj.value || null; this.lChild = obj.lChild || null; this.lTag = obj.lTage || 0; //0表示指向结点,1表示线索 this.rChild = obj.rChild || null; this.rTag = obj.rTag || 0; } function visit(binaryNode){ if (binaryNode.val) { console.log(binaryNode.val); } } // 对双向线索表进行中序遍历 function InOrderTraverse(biThrTree){ //biThrTree指向头结点 var tmp = biThrTree.lChild; while (tmp!==biThrTree) { while (!tmp.lTag) { tmp = tmp.lChild; } visit(tmp); while (tmp.rTag && tmp!==biThrTree) { tmp=tmp.rChild; visit(tmp); } tmp=tmp.rChild; } return biThrTree; }
线索化二叉树
//线索化二叉树 function InOrderThreading(biThrTree,biTree){ biThrTree = new BinaryThrNode(); biThrTree.lChild = biTree; biThrTree.rTag = 1; biThrTree.rChild = biThrTree; if (!biTree) { biThrTree.lTag = 1; biThrTree.lChild = biThrTree; }else { biThrTree.lChild = biTree; var pre = biThrTree; InThreading(biTree); pre.rChild = biThrTree; pre.rTag = 1; biThrTree.rChild = pre; } return biThrTree; } function InThreading(binaryNode){ if (binaryNode) { InThreading(binaryNode.lChild); if (!binaryNode.lChild) { binaryNode.lTag = 1; p.lChild = pre; } if (!pre.rChild) { pre.rTag = 1; pre.rChild = binaryNode; } InThreading(binaryNode.rChild); } }
赫夫曼树及其应用
赫夫曼树又称最优树,是一种带权路径长度最短的树。最优二叉树
树的路径长度是从树的根到每一个结点长度的和。 树的带权路径长度为树中所有叶子结点的带权路径长度之和。根据n个权值{w1, w2, ⋯,wn},构造成n棵二叉树的集合F={T1, T2, ⋯,Tn},其中每棵二叉树只有一个权值为wi的根结点,没有左、右子树;
在F中选取两棵根结点权值最小的树作为左、右子树构造一棵新的二叉树,且新的二叉树根结点权值为其左、右子树根结点的权值之和;
在F中删除这两棵树,同时将新得到的树加入F中;
重复2,3,直到F只含一棵树为止。
构造Huffman树时,为了规范,规定F={T1,T2, ⋯,Tn}中权值小的二叉树作为新构造的二叉树的左子树,权值大的二叉树作为新构造的二叉树的右子树;在取值相等时,深度小的二叉树作为新构造的二叉树的左子树,深度大的二叉树作为新构造的二叉树的右子树。
赫夫曼编码
若要设计长短不等的编码,则必须是任一个字符的编码都不是另一个字符的编码的前缀,这种编码称为前缀编码。// 赫夫曼树和赫夫曼编码的存储结构 function HuffmanNode(weight, parent, leftChild, rightChild) { this.weight = weight || 0; this.parent = parent || 0; this.leftChild = leftChild || 0; this.rightChild = rightChild || 0; } // 创建一棵叶子结点数为n的Huffman树 function buildHuffmanTree(weights, n) { n = n || weights.length; var m = 2 * n - 1; var huffmanTree = []; // 初始化 for (var i = 0; i < n; i++) huffmanTree[i] = new HuffmanNode(weights[i], 0, 0, 0); for (; i < m; i++) huffmanTree[i] = new HuffmanNode(0, 0, 0, 0); for (i = n; i < m; i++) { // 在HT[1..i-1]选择parent为0且weight最小的两个结点,返回其序号为[s1, s2] var ret = select(huffmanTree, i); var s1 = ret[0]; var s2 = ret[1]; huffmanTree[s1].parent = i; huffmanTree[s2].parent = i; huffmanTree[i].leftChild = s1; huffmanTree[i].rightChild = s2; huffmanTree[i].weight = huffmanTree[s1].weight + huffmanTree[s2].weight; } return huffmanTree; } function select(huffmanTree, len) { var ret = []; for (var i = 0; i < len; i++) { var node = huffmanTree[i]; if (node.parent !== 0) continue; if (ret.length < 2) { ret.push(i); } else { var index = huffmanTree[ret[0]].weight > huffmanTree[ret[1]].weight ? 0 : 1; if (node.weight < huffmanTree[ret[index]].weight) ret[index] = i; } } if (ret[0] > ret[1]) { var temp = ret[0]; ret[0] = ret[1]; ret[1] = temp; } return ret; }
参考资料
javascript实现数据结构: 树和二叉树,二叉树的遍历和基本操作树和二叉树的应用
相关文章推荐
- POJ_3045_Cow_Acrobats_(贪心)
- Spark机器学习(一) -- Machine Learning Library (MLlib)
- ios layer 动画-(transform.rotation篇)
- POJ_3111_K_Best_(二分,最大化平均值)
- 算是内心独白吧!
- 联合体 union
- 分组交换
- POJ_3104_Drying_(二分,最小化最大值)
- 《T-SQL查询》- SQL逻辑处理
- 浏览器插件之ActiveX开发 资料收集
- POJ_3273_Monthly_Expense_(二分,最小化最大值)
- 个人工作总结05
- NYOJ 523 亡命逃窜(三维BFS)
- 腾讯云CentOS7安装LNMP+wordpress
- 练习2————1015
- [BZOJ4518][Sdoi2016]征途(斜率优化dp)
- 浅入tcp
- 【014】【JVM中的两个Survivor区】
- Java的String char()[待补充]
- wincmd for windows