数据结构与算法—二叉树(tree)
Posted 为了维护世界和平_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构与算法—二叉树(tree)相关的知识,希望对你有一定的参考价值。
目录
二叉树
如何存储一颗二叉树
1、使用链表,链表的节点存储 左右节点+数据
2、使用数组的顺序存储法,(左子节点 2*i 右子节点 2*i+1 仅仅浪费了下标为0的存储位置)
如果不是完全二叉树,浪费的空间比较多。
二叉树的遍历:前序遍历,中序遍历,后序遍历;
二叉查找树
树的任意一个节点,左子树的值小于这个节点的值,而右子树的值,大于这个节点的值。
1、二叉树的查找
- 先取根节点,如果根节点等于要查找的数据,返回;
- 如果根节点数据比要查找的数小,那就在右子树中递归查找;否则在左子树中递归查找;
struct node *find(struct root *root,unsigned long data)
struct node * n = root->r;
while(n)
if(n->data == data)
return n;
if(data < n->data)
n = n->left;
else
n = n->right;
return NULL;
2、二叉树的插入
- 如果插入的数据比节点数据大,并且节点的右子树为空,就将新数据直接插到右节点位置;
- 如果右节点不为空,再次递归遍历右子树,查找插入位置。
- 同理,如果要插入的数据比节点数小,并且左子树为空,插入到左子树的位置。
- 如果不为空,就再遍历左子树,找到插入位置。
void insert(struct root *root,struct node *new)
struct node *parent;
if(!root->r)
root->r = new;
return;
parent = root->r;
while(1)
if(new->data == parent->data)
break;
if(new->data < parent->data)
if(!parent->left)
parent->left = new;
break;
parent = parent->left;
else
if(!parent->right)
parent->right = new;
break;
parent = parent->right;
3、二叉树的删除
- 如果要删除的节点只有一个子节点,只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点就可以了。
- 如果要删除的节点没有子节点,只需要直接将父节点中,指向要删除节点的指针为Null
- 如果要删除的节点有两个子节点。需要找到这个节点的右子树中的最小节点,把它替换到要删除的节点上,然后再删除这个最小节点;因为最小节点肯定没有左子节点。
struct node * delete(struct root *root,unsigned long data)
struct node *n = root->r,**p = &root->r;
struct node *child;
while(n && n->data != data)
if(data < n->data)
p = &n->left;
n = n->left;
else
p = &n->right;
n = n->right;
if(!n)
return NULL;
if(n->left && n->right)
struct node *rn = n->right,**rp = &n->right;
while(rn->left)
rp = &rn->left;
rn = rn->left;
n->data = rn->data;
n = rn;
p = rp;
child = n->left ? n->left : n->right;
*p = child;
return NULL;
中序遍历二叉查找树,可以输出有序的数据序列,时间复杂度O(n),非常高效。
与散列表相比,二叉树的优势
- 散列表的数据时无序的,如果要输出有序数据,需要排序;而二叉树,只需要中序遍历,可以在O(n)的时间复杂度内,输出有序序列
- 散列表扩容耗时多,遇到冲突时,性能不稳定;平衡二叉查找树性能非常稳定,时间复杂度O(logn)
- 散列表查找操作时间复杂度是常量级,但哈希冲突存在,常量不一定比logn小。查找速度不一定比o(logn)块,加上哈希的耗时,也就不一定有平衡二叉树效率高。
- 散列表的构造比二叉树要复杂。散列函数,冲突解决,扩容等。平衡二叉查找树值需要考虑平衡问题,方案成熟稳定。
- 避免散列冲突,散列装载因子不能太大,特别是开放寻址法解决冲突,不然会浪费时间。
二叉查找树存在的问题
二叉查找树在频繁的动态更新过程中,可能会出现树的高度远大于logn的情况,从而导致各个操作的效率下降。在极端情况下,二叉树会退化为链表。时间复杂度O(n)
解决复杂度退化问题,需要设计一种平衡二叉查找树,红黑树
平衡二叉树(AVL)
平衡二叉树特点
- 任意一个节点的左右子树的高度相差不能大于1
- 完全二叉树、满二叉树都是平衡二叉树
- 非完全二叉树也有可能是平衡二叉树
查找效率很高,但是维护平衡,每次插入删除都要做调整,复杂 耗时
红黑树
它是一种不严格的平衡二叉查找树,维护成本比AVL树低。
树中的节点,一类被标记为黑色,一类被标记为红色
红黑树特点
- 根节点是黑色
- 每个叶子节点都是黑色的空节点,也就是说,叶子节点不存储数据。
- 任何相邻的节点都不能同时为红色;
- 每个节点,从该节点到达其叶子节点的所有路径,都包含相同数目的黑色节点。
红黑树的高度近似2logn,比AVL树高度大了一倍,在性能上下降并不多
几种二叉树的比较
二叉查找树: 左节点<其节点<右节点,左右子树的高度差可能很大,最大退化成链表
平衡二叉树:避免左右子树高度相差不能大于1,过多的调整 O(logn)
红黑树:不规范的平衡二叉树,根节点是黑色的;
- 叶子节点都是黑色的空节点;任何相邻的节点都不能同时为红色;
- 每个字节到达其可达叶子节点的所有路径,都包含相同数据的黑色节点;
- 插入、删除、查找的时间复杂度O(logn);保证每次插入最多只用三次旋转就能达到平衡。
堆:顶点大于或者小于左右子树
以上是关于数据结构与算法—二叉树(tree)的主要内容,如果未能解决你的问题,请参考以下文章