树的常用术语的相关定义
树:树是n(n>=0)个节点的有限集。
度:结点拥有的子树称为结点的度。度为0 的结点称为叶子结点或终端结点。不为0的结点称为非终端结点或分枝结点。初了根结点之外,分支结点也称为内部结点。(如图1,结点1的度是3,2的度是2。)
树的度:树的度是树内各结点的度的最大值。(如图1,树的度是3。)
结点之间关系的定义:结点的子树的根称为该结点的孩子,相应地,该结点称为孩子的双亲。同一个双亲的孩子之间互称为兄弟。结点的祖先是从根到该结点所经分枝上 的所有结点。反之,以某结点为根的子树中的任一结点都称为该结点的子孙。
结点的层次:从根开始,根为第一层,根的孩子为第二层,以此类推… 。双亲在同一层的结点互为堂兄弟。
树的深度:树中结点的最大层次称为树的高度或深度。(如图1 树的深度为3)
有序树和无序树:如果将树中结点的各子树看成是从左至右是又次序的,则称该树为有序树,否则为无序树。(如图1 为无序树 图2为有序树)
二叉树
定义: 二叉树是另一种树型结构, 它的特点是每个结点至多只有两颗子树(即二叉树中不存在度大于2的结点),并且二叉树的子树有左右之分,其次序不能任意颠倒。(如图1 不是二叉树 图2 是二叉树)
性质:
性质 1. 在二叉树的地i层最多有 2 ^(i-1) 个结点 i>=1。
性质 2. 深度为k的二叉树至多有2^k - 1个结点 k>=1。
性质 3. 对任何一颗二叉树T,如果其终端结点数为n0,度为2的结点数为n2,那么n0=n2+1。
完全二叉树和满二叉树是两种特殊形态的二叉树。
满二叉树定义:一颗深度为k且有2^k - 1个节点的二叉树称为满二叉树。(如图3为满二叉树)
完全二叉树定义:深度为k的,有n个结点的二叉树,当且仅当其中每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称之为完全二叉树。 (如图4为完全二叉树)
性质 4. 具有n个结点的完全二叉树的深度为log2n + 1
证明:假设深度为k,则根据性质2和完全二叉树的定义有 2^(k-1) - 1 < n <= 2^k - 1 故 k-1<= log2n < k ,k是整数,所以k=log2n+1
性质 5. 如果一颗有n个结点的完全二叉树的结点按层序编号,对任意结点有:
5.1. 如果i = 1,则结点i是二叉树的根,无双亲;如果i>1 ,则双亲是i/2。
5.2. 如果2i > n, 则结点i无左孩子;否则其左孩子的结点是2i。
5.3. 如果2i + 1 > n, 则结点i无右孩子,否则其右孩子的结点是2i + 1。
遍历二叉树
二叉树遍历分为三种:先序遍历、中序遍历、后序遍历
先序遍历操作:(1)访问根结点;(2)先序遍历左子树;(3)先序遍历右子树; (图4为例遍历后123456)
中序遍历操作:(1)先序遍历左子树;(2)访问根结点;(3)先序遍历右子树; (图4为例遍历后324165)
后序遍历操作:(1)先序遍历左子树;(2)先序遍历右子树;(3)访问根结点; (图4为例遍历后342651)
1. 递归(中序遍历)时间复杂度O(n) 空间复杂度 0
struct TreeNode {
int data;
TreeNode *left;
TreeNode *right;
};
void middle_order(TreeNode *Node) {
if(Node != NULL) {
middle_order(Node->left);
printf("%d ", Node->data);
middle_order(Node->right);
}
}
2.非递归(中序遍历)时间复杂度O(n) 空间复杂度 最坏要O(log2n + 1)
//中序遍历
void middle_order(TreeNode* root)
{
//空树
if (root == NULL)
return;
//树非空
TreeNode* Node = root;
stack<TreeNode*> s;
while (!s.empty() || Node)
{
while (Node)
{
s.push(Node);
Node = Node->left;
}
if (!s.empty())
{
Node = s.top();
s.pop();
printf("%d ", Node->data);
Node = Node->right;
}
}
}
赫夫曼树
假设有n个权值 {w1, w2, w3, …, wn},试构造一颗有n个叶子结点的二叉树,每个叶子结点带权为wi, 则其中带权路径长度WPL最小的二叉树称为最优二叉树或赫夫曼树。
如图6 的3颗二叉树,都有4个叶子结点abcd,带权分别为7、 5、 2、 4。他们的带权路径长度分别为。
(1)WPL = 7*2 + 5*2 + 2*2 + 4*2 = 36
(2)WPL = 。。。。 = 46
(3)WPL = 。。。。 = 35
如何构建赫夫曼树?根据给定的权值取最小的两个作为叶子结点,把这两个叶子结点的根结点作为新的结点权值为两个叶子结点权值的和,以此类推。。。
二叉排序树(又称二叉查找树)
性质:(1) 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值; (3)左、右子树也分别为二叉排序树;
二叉排序树的查找:若根结点的关键字值等于查找的关键字,成功。否则如果小于关键字,查左子树,大于关键字查右子树。
平均查找长度计算方式:时间复杂度 log(n)
排序二叉树的创建:
struct BiTree {
int data;
BiTree *lchild;
BiTree *rchild;
};
//在二叉排序树中插入查找关键字key
BiTree* InsertBST(BiTree *t,int key) {
if (t == NULL) {
t = new BiTree();
t->lchild = t->rchild = NULL;
t->data = key;
return t;
}
if (key < t->data)
t->lchild = InsertBST(t->lchild, key);
else
t->rchild = InsertBST(t->rchild, key);
return t;
}
//n个数据在数组d中,tree为二叉排序树根
BiTree* CreateBiTree(BiTree *tree, int d[], int n) {
for (int i = 0; i < n; i++)
tree = InsertBST(tree, d[i]);
}
排序二叉树的查找
struct BiTree {
int data;
BiTree *lchild;
BiTree *rchild;
};
//递归查找
BiTree* findBST(BiTree *t, int key) {
if (t) {
if(key == t->data)
return t;
else if(key < t->data)
return findBST(t->lchild, key);
else
return findBST(t->rchild, key);
}
return null;
}
//非递归查找
BiTree* findBST(BiTree *t, int key) {
BiTree *p = t;
while (p) {
if (p->data==key) {
return p;
break;
} else if (p->data>key) {
p=p->lchild;
} else{
p=p->rchild;
}
}
return null;
}
排序二叉树的删除
排序二叉树的删除比较复杂 有四种情况:
1. 节点无子节点时 直接删除 (如图(a))
2. 节点只有左子树时 直接用左孩子节点替换被删除节点 (如图(b))
3. 节点只有右子树时 直接用右孩子节点替换被删除节点 (类似图(b))
4. 节点左右子树都存在时 我们可以用被删除的节点的前驱 或者后继 代替该结点 (如图(c))
4.1 当用前驱代替时,若被删除节点的左子节点无右子树,直接用左子节点替换被删除节点,将被删除节点的左指针指向子左节点的左子树(如图c1)。若被删除节点左子节点有右子树,循环查找到被删节点的前驱,也就是左子树的最右下方的节点,替换为被删节点,将最右下方的节点的父节点的右指针指向最右下方节点左孩子(如图c2)。( 如下代码是按照前驱代替被删节点实现的)
4.2 当用后继代替时。。。 大家自己去想一下。
代码如下:
bool deleteTree(BTree **b,int key) {
if(!*b)
return false;
else {
if((*b)->data == key){
return deleteNode(&(*b));
}
else if((*b)->data > key)
return deleteTree(&(*b)->lchild,key);
else
return deleteTree(&(*b)->rchild,key);
}
}
bool deleteNode(BTree **b) {
BTree *p,*s;
if((*b)->lchild == NULL ){
p = (*b);
(*b) = (*b)->rchild;
free(p);
}else if((*b)->rchild == NULL){
p = (*b);
(*b) = (*b)->lchild;
free(p);
}else{
p = (*b);
s = (*b)->lchild;
while(s->rchild != NULL){
p = s;
s = s->rchild;
}
(*b)->data = s->data;
if (p != *b)
p->rchild = s->lchild; // 用b的前驱替换b
else
p->lchild = s->lchild;
free(s);
return true;
}
}
参考教材:《 数据结构 》清华大学出版社 严蔚敏 吴伟民著