树: 能提高数据的存储, 读取的效率
例如二叉排序树
既可以保证数据的检索速度, 还能保证数据的插入, 删除, 修改的速度
遍历二叉树的三种方式
看父节点的输出顺序
有前序, 中序, 和后序
前序: 先输出父节点, 再遍历左子树和右子树
中序: 先遍历右子树, 在输出父节点, 最后遍历右子树
后序: 先遍历左子树, 再遍历右子树, 最后输出父节点
示例
手动创建一个这样的二叉树, 并遍历
public class ErChaShuTest {
public static void main(String[] args) {
//手动创建二叉树
NBA root = new NBA(3, "韦德");
root.left = new NBA(1, "麦迪");
root.right = new NBA(13, "哈登");
root.right.left = new NBA(6, "詹姆斯");
root.right.right = new NBA(24, "科比");
//创建二叉树对象
NbaErChaShu nbaErChaShu = new NbaErChaShu(root);
//分别使用前序, 中序后序进行遍历
nbaErChaShu.qianxu();
nbaErChaShu.zhongxu();
nbaErChaShu.houxu();
}
}
//树节点
class NBA{
public int id;
public String name;
public NBA left;
public NBA right;
public NBA(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "NBA{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//前序遍历
public void qianxu(){
System.out.println(this);
if (this.left!= null){
this.left.qianxu();
}
if (this.right!= null){
this.right.qianxu();
}
}
//中序
public void zhongxu(){
if (this.left!= null){
this.left.zhongxu();
}
System.out.println(this);
if (this.right!= null){
this.right.zhongxu();
}
}
//后序
public void houxu(){
if (this.left!= null){
this.left.houxu();
}if (this.right!= null){
this.right.houxu();
}
System.out.println(this);
}
}
//二叉树
class NbaErChaShu{
private NBA root;
public NbaErChaShu(NBA root) {
this.root = root;
}
//前序遍历
public void qianxu(){
if (this.root == null){
System.out.println("空树");
return;
}
System.out.println("前序遍历结果如下: ");
root.qianxu();
}
//中序遍历
public void zhongxu(){
if (this.root == null){
System.out.println("空树");
return;
}
System.out.println("中序遍历结果如下: ");
root.zhongxu();
}
//后序遍历
public void houxu(){
if (this.root == null){
System.out.println("空树");
return;
}
System.out.println("后序遍历结果如下: ");
root.houxu();
}
}
前序遍历结果如下:
NBA{
id=3, name='韦德'}
NBA{
id=1, name='麦迪'}
NBA{
id=13, name='哈登'}
NBA{
id=6, name='詹姆斯'}
NBA{
id=24, name='科比'}
中序遍历结果如下:
NBA{
id=1, name='麦迪'}
NBA{
id=3, name='韦德'}
NBA{
id=6, name='詹姆斯'}
NBA{
id=13, name='哈登'}
NBA{
id=24, name='科比'}
后序遍历结果如下:
NBA{
id=1, name='麦迪'}
NBA{
id=6, name='詹姆斯'}
NBA{
id=24, name='科比'}
NBA{
id=13, name='哈登'}
NBA{
id=3, name='韦德'}
查找
三种查找方式
前序中序后序
并分析各种查找分别比较了多少次
public class ErChaShuTest {
public static void main(String[] args) {
//手动创建二叉树
NBA root = new NBA(3, "韦德");
root.left = new NBA(1, "麦迪");
root.right = new NBA(13, "哈登");
root.right.left = new NBA(6, "詹姆斯");
root.right.right = new NBA(24, "科比");
//创建二叉树对象
NbaErChaShu nbaErChaShu = new NbaErChaShu(root);
//分别使用前序, 中序后序进行查找
nbaErChaShu.qianxuchazhao(6);
nbaErChaShu.zhongxuchazhao(6);
nbaErChaShu.houxuchazhao(6);
}
}
//树节点
class NBA{
public int id;
public String name;
public NBA left;
public NBA right;
public NBA(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "NBA{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//前序查找
public NBA qianxuchazhao(int no){
System.out.println("次数");
if (this.id == no){
return this;
}
NBA res = null;
if (this.left != null){
res = this.left.qianxuchazhao(no);
}
if (res != null){
return res;
}
if (this.right != null){
res = this.right.qianxuchazhao(no);
}
return res;
}
//中序查找
public NBA zhongxuchazhao(int no){
NBA res = null;
if (this.left != null){
res = this.left.zhongxuchazhao(no);
}
if (res != null){
return res;
}
System.out.println("次数");
if (this.id == no){
return this;
}
if (this.right != null){
res = this.right.zhongxuchazhao(no);
}
return res;
}
//后序查找
public NBA houxuchazhao(int no){
NBA res = null;
if (this.left != null){
res = this.left.houxuchazhao(no);
}
if (res != null){
return res;
}
if (this.right != null){
res = this.right.houxuchazhao(no);
}
if (res != null){
return res;
}
System.out.println("次数");
if (this.id == no){
res = this;
}
return res;
}
}
//二叉树
class NbaErChaShu{
private NBA root;
public NbaErChaShu(NBA root) {
this.root = root;
}
//前序查找
public void qianxuchazhao(int no){
if (root == null){
System.out.println("空树");
return;
}
NBA res = root.qianxuchazhao(no);
if (res == null){
System.out.println("树中没有该球员");
return;
}
System.out.println("号码为"+no+"的球员是"+res);
return;
}
//中序查找
public void zhongxuchazhao(int no){
if (root == null){
System.out.println("空树");
return;
}
NBA res = root.zhongxuchazhao(no);
if (res == null){
System.out.println("树中没有该球员");
return;
}
System.out.println("号码为"+no+"的球员是"+res);
return;
}
//后序查找
public void houxuchazhao(int no){
if (root == null){
System.out.println("空树");
return;
}
NBA res = root.houxuchazhao(no);
if (res == null){
System.out.println("树中没有该球员");
return;
}
System.out.println("号码为"+no+"的球员是"+res);
return;
}
}
删除节点
顺序存储二叉树
线索化二叉树
将如图所示的二叉树使用中序的方式线索化
public class XiansuohuaTest {
public static void main(String[] args) {
//测试中序线索化: 4,2,5,1,6,3
Nnode nnode1 = new Nnode(1, "科比");
Nnode nnode2 = new Nnode(2, "詹姆斯");
Nnode nnode3 = new Nnode(3, "韦德");
Nnode nnode4 = new Nnode(4, "波什");
Nnode nnode5 = new Nnode(5, "大鸟");
Nnode nnode6 = new Nnode(6, "乔丹");
nnode1.setLeft(nnode2);
nnode1.setRight(nnode3);
nnode2.setLeft(nnode4);
nnode2.setRight(nnode5);
nnode3.setLeft(nnode6);
XiansuohuaErchashu xiansuohuaErchashu = new XiansuohuaErchashu(nnode1);
xiansuohuaErchashu.zhongxuXiansuohua(nnode1);
System.out.println("4号节点的前驱和后继节点是");
System.out.println(nnode4.getLeft());
System.out.println(nnode4.getRight());
System.out.println("5号节点的前驱和后继节点是");
System.out.println(nnode5.getLeft());
System.out.println(nnode5.getRight());
System.out.println("6号节点的前驱和后继节点是");
System.out.println(nnode6.getLeft());
System.out.println(nnode6.getRight());
System.out.println("3号节点的后继节点是");
System.out.println(nnode3.getRight());
System.out.println("中序遍历的最后一个节点的righttype是不会等于1的");
System.out.println(nnode3.getRighttype());
}
}
//实现了线索化的二叉树:
class XiansuohuaErchashu{
public Nnode root;
public Nnode pre = null; //为了实现线索化而创建的一个辅助指针指向前驱节点
public XiansuohuaErchashu(Nnode root) {
this.root = root;
}
//中序线索化
/**
* 这个nnode就是当前需要线索化的节点
* @param nnode
*/
public void zhongxuXiansuohua(Nnode nnode){
//如果nnode是null, 不能线索化, 直接return
if (nnode == null){
return;
}
//1. 先线索化左子树
zhongxuXiansuohua(nnode.getLeft());
//2. 再线索化当前节点
if (nnode.getLeft() == null){
nnode.setLeft(pre);
nnode.setLefttype(1);
}
if (pre != null && pre.getRight() == null){
pre.setRight(nnode);
pre.setRighttype(1);
}
pre = nnode;
//3. 再线索化右子树
zhongxuXiansuohua(nnode.getRight());
}
}
//树节点
class Nnode{
public int id;
public String name;
public Nnode left;
public Nnode right;
//设置前继节点和后继节点的判断哪标识
public int lefttype = 0;
public int righttype = 0;
//构造器
public Nnode(int id, String name) {
this.id = id;
this.name = name;
}
//getandset
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Nnode getLeft() {
return left;
}
public void setLeft(Nnode left) {
this.left = left;
}
public Nnode getRight() {
return right;
}
public void setRight(Nnode right) {
this.right = right;
}
public int getLefttype() {
return lefttype;
}
public void setLefttype(int lefttype) {
this.lefttype = lefttype;
}
public int getRighttype() {
return righttype;
}
public void setRighttype(int righttype) {
this.righttype = righttype;
}
@Override
public String toString() {
return "Nnode{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
4号节点的前驱和后继节点是
null
Nnode{
id=2, name='詹姆斯'}
5号节点的前驱和后继节点是
Nnode{
id=2, name='詹姆斯'}
Nnode{
id=1, name='科比'}
6号节点的前驱和后继节点是
Nnode{
id=1, name='科比'}
Nnode{
id=3, name='韦德'}
3号节点的后继节点是
null
中序线索化的最后一个节点的righttype是不会等于1的
0
遍历线索化的二叉树
由于经过线索化之后, 原本的三种遍历方式已经不能用了, 所以必须有新的遍历方式来遍历线索化之后的二叉树, 这个新的方法不是使用的递归, 而是使用的线性的方式. 所以提高了效率
前面这个数是使用的中序的方式进行的线索化, 所以遍历出来的结果也应当是中序遍历的结果
中序遍历中序线索化的二叉树代码
public class XiansuohuaTest {
public static void main(String[] args) {
//测试中序线索化: 4,2,5,1,6,3
Nnode nnode1 = new Nnode(1, "科比");
Nnode nnode2 = new Nnode(2, "詹姆斯");
Nnode nnode3 = new Nnode(3, "韦德");
Nnode nnode4 = new Nnode(4, "波什");
Nnode nnode5 = new Nnode(5, "大鸟");
Nnode nnode6 = new Nnode(6, "乔丹");
nnode1.setLeft(nnode2);
nnode1.setRight(nnode3);
nnode2.setLeft(nnode4);
nnode2.setRight(nnode5);
nnode3.setLeft(nnode6);
XiansuohuaErchashu xiansuohuaErchashu = new XiansuohuaErchashu(nnode1);
xiansuohuaErchashu.zhongxuXiansuohua(nnode1);
xiansuohuaErchashu.zhongxuXiansuohuaList();
}
}
//实现了线索化的二叉树:
class XiansuohuaErchashu{
public Nnode root;
public Nnode pre = null; //为了实现线索化而创建的一个辅助指针指向前驱节点
public XiansuohuaErchashu(Nnode root) {
this.root = root;
}
//中序线索化
/**
* 这个nnode就是当前需要线索化的节点
* @param nnode
*/
public void zhongxuXiansuohua(Nnode nnode){
//如果nnode是null, 不能线索化, 直接return
if (nnode == null){
return;
}
//1. 先线索化左子树
zhongxuXiansuohua(nnode.getLeft());
//2. 再线索化当前节点
if (nnode.getLeft() == null){
nnode.setLeft(pre);
nnode.setLefttype(1);
}
if (pre != null && pre.getRight() == null){
pre.setRight(nnode);
pre.setRighttype(1);
}
pre = nnode;
//3. 再线索化右子树
zhongxuXiansuohua(nnode.getRight());
}
//遍历中序线索化之后的二叉树
public void zhongxuXiansuohuaList(){
Nnode cur = root;
while(cur != null){
//当前节点若为空了, 直接退出
while (cur.getLefttype() == 0){
//一直向左找到中序遍历的第一个节点
cur = cur.getLeft();
}
System.out.println(cur); //直接打印当前节点
while(cur.getRighttype() == 1){
//当前节点若有后继节点, 直接跳过去并打印, 如果没有进这个while说明当前节点有右节点, 也是直接跳过去
cur = cur.getRight();
System.out.println(cur);
}
cur = cur.getRight(); //直接跳过去右节点, 但是这里就要重新开始判断, 是不是null, 表示最后了退出, 不是最后是不是有左子树, 有得先进左子树
}
}
}
Nnode{
id=4, name='波什'}
Nnode{
id=2, name='詹姆斯'}
Nnode{
id=5, name='大鸟'}
Nnode{
id=1, name='科比'}
Nnode{
id=6, name='乔丹'}
Nnode{
id=3, name='韦德'}