二叉树简介

前言

对于有序数组,有查询速度快的优势可以直接用二分法查询效率高,但是插入或者删除某一个元素效率低,因为数组的查询之所以快是因为内存连续然后可以通过下标即指针直接寻找所以很快,所以当删除或者插入到数组中间时,需要之后的所有元素往后移动再插入来保证数组的有序和连续性才能使用高效的查找。
对于有序链表,保存是按顺序保存的,两边分别为极值,有序链表有插入和删除快的优点,因为只需要修改一个引用就可以了,但是查询速度很慢,虽然也是有序的但是因为链表种只保存一个指向后面元素的引用所以查询特定元素必须从链表头一直指向下一个进行一个一个对比,查询效率和无序链表一样,有序链表可以遍历出来有序的数据。
这两种数据结构都很不理想,要么查询速度太慢了,要么插入速度太慢了,贼慢那种,想用数组的高速查询又讨厌数组的插入和删除太慢(因为要维护顺序所以插入中间是有可能的)这种情况如果在插入和删除少查询多的时候还可以用用,想用链表插入和删除速度快有讨厌其中查询的蜗牛速度(因为只能仅仅沿着链一个一个爬)这种情况如果在插入和删除多查询少的时候还可以用用。
这时候二叉树就上场了,二叉树即有有序数组那样的查询速度也有链表那种一样的插入速度,理想的数据结构。
理想是不可能存在的,这辈子都不可能有理想的数据结构的,这种结构总是会牺牲一些代价的,在插入随机的数的时候是比较理想的但是当插入的数据是有顺序的排序好的话就和一个链表一个样就是一条链,丧失了快速的查询速度,这种情况为失去平衡,当然解决办法总是有的,就是后面的平衡树的出现,比如最常用的红黑树,还有就是删除贼麻烦……
这个二叉树只具有嘛这么一个致命的缺点有那么多待完善的地方,当然不会有人用了,用的都是红黑树B树啥的,不过具有教学价值,因为好多树是以这个为基础进行改进的学这个基础才能更好的学习后面的树,像冒泡排序算法一样,实际上大家都不用但是大家都懂。

二叉树增删查遍历

下图就是一颗二叉树
在这里插入图片描述

  • 二叉搜索树
    左节点小于右节点的二叉树,有了这个规则,插入,查找,最大最小值都很简单得到。若不是搜索树则都离不开遍历进行这些操作

  • 二叉搜索树插入和查找
    插入和查找过程类似,都是沿着树的节点找,查找就是从根节点开始,跟根节点比较若大于则进入右节点进行比较,小于则进入左节点,直到找到相等为止,或者指向为空节点则不存在。而插入则做同样的比较,当下一个节点指向空节点时即为插入的位置。

  • 二叉搜索树删除节点
    -下面都是不为父节点情况下,父节点需要额外的考虑
    若删除节点为叶节点(没有子节点的节点),直接将父节点指向空即可。
    若删除节点只有一个子节点,这种也不麻烦,只需要将父节点中指向删除节点的引用修改成指向删除节点的唯一直子节点即可
    若删除节点有两个子节点就比较麻烦了,这种情况需要找一个合适的节点代替删除节点的位置,可以是左节点中最大的节点也可以是右节点中最小的节点。当找到的这个要拿来代替的节点有一个子节点(只能有一个和没有)或者没有子节点就相当于把该节点按上面的第一和第二中情况做删除处理即可然后拿去代替。
    删除比较麻烦所以一般会尽量避免做删除操作,有时候可以只是做一个标记(表示该节点已经被删除)实际上还存在,只是不可用了,这样可以避免删除的麻烦操作。

  • 遍历
    遍历树一般用递归的方式遍历因为如果不用递归的话要保存各种父节点信息,每进入一个地方要先保存父节点再进入子节点,所以递归就比较简单不用我们在程序里写保存父节点信息,非递归方式遍历也是通过栈实现,递归分析非递归实现(用栈)可以查看《递归想法和实现介绍,消除递归》这一遍的消除递归部分进行非递归实现遍历。
    递归遍历思路,这里以中序遍历为例,将一个节点传入方法,调用自身来遍历左节点,输出当前节点,调用自身来遍历右节点,遍历完毕。
    看看递归实现遍历的代码

    public void inOrder(node localRoot){
    	if(localRoot!=null){
    		inOrder(localRoot.leftChild);
    		System.out.println(localRoot.data);
    		inOrder(localRoot.rightChild);
    	}
    }
    

    还有前序和后序遍历,就是输出当前节点的位置为调用自身遍历左右节点的前中后划分的。各自都有特别有用的地方,中序遍历是最常用的。

  • 效率
    显然理想的二叉搜索树的效率插入和删除都为O(logN),因为插入和查找都需要比较的字数和树的高度一样而树高度在理想情况下为logN,当不平衡时就不是这样理想了。

  • 哈夫曼编码
    上面讲的都是搜索二叉树,只是说最常用的是搜索二叉树,并不是说普通的二叉树没用,比如代数表达式就用上后序遍历,,还比如要讲的一种压缩数据的编码哈曼夫编码,
    压缩文本最常用的方法就是通过减少表示字符的位数实现,比如常规的字符为8位的二进制表示,但是如果文本只有小写的英文的话根本不用那么多位英文只有26个字母所以只需要5位二进制可以表示32个状态就可以表示完所有的小写英文字母,但是文本不仅有小写还有大写还有标点符号,如果假设都有的话就没办法压缩了吗?有的办法总会有的,哈夫曼发现,虽然所有的字符都有不能减少位数,但是有的字符出现次数少有的比较多,比如Java代码中;这个符号就出现比较多了,于是就提出解决方案就是出现次数多的用位数效少来表示少的用位数多表示,有的位数变多了有的位数变少了,根据出现的频率选择表示的位数。这样同样能达到不错的压缩效果,这种即为哈夫曼编码的可变字长编码思想,怎么实现呢?当然是二叉树了,二叉树就有两个叉,刚好二进制也只有两个状态,完美符合。怎么通过字符出现的频率合理的建立二叉树然后知道用几位二进制表示达到压缩效果呢?
    最后的效果是这样的
    在这里插入图片描述
    圆圈节点上面的数字是节点下所有字符出现的频率,说明整个文档只有22个字符,SP空格出现的频率是4,等。这棵树怎么用呢,先是从顶点往下,左边便是0右边表示1,所到的字符便是该字符的二进制编码比如空格SP编码00,S编码10,Y编码1110,U编码01111,达到了频率越高表示的二进制位数越短,即达到压缩的效果。这样的树怎么建立的呢,如下图一样,先遍历整个文本,统计所有字符的频率构成节点节点有两个数据项,字符本身和频率,还有左右节点的引用,将节点按频率大小放入一个优先队列里如下图,然后弹出两个节点,创建一个字符为空频率为两个节点的和的节点然后左右节点的引用分别为刚刚弹出的节点,然后将该节点插入回队列里,再弹出两个节点,重复同样的操作,直到最后只剩下一个节点就是哈夫曼树,用这颗树进行编码和解码即可达到压缩的效果。每个文本都对应一颗这样的树。
    在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值