java数据结构与算法 --- 第十章 数结构基础
创始人
2024-02-16 03:10:05

第十章 树结构基础

I 引和基本概念

为什么需要树结构?
数组,查询快,增删慢
链表…
而树结构,同时提高查询和增删!

基本概念
术语: 有手就行

II 二叉树

1.概念:

二叉树:每个节点最多有两个子节点的数叫二叉树
满二叉树: 所有叶子节点都在最后一,结点的总数是2^n-1(n是层数)
完全二叉树:这个概念…怎么说呢,叶子几点只在最后或者倒数第二层,并且最后一层都是左边排列

完全二叉树看起来不完全,为什么叫做完全二叉树呢?
因为,涉及到顺序存储,空间充分利用

2.前中后序遍历,查找

前中后序,针对的是父节点的遍历时机(中序为例:左子节点遍历,父节点,右子结点遍历)

思路:
i 节点类里面完成前中后序遍历的具体操作操作
ii 创建二叉树类,完成操作管理,创建二叉树确定具体的根节点
iii 测试类,手动创建二叉树

上代码:

public class BinaryTreeDemo {@Testpublic void test() {Node root1 = new Node(1, "1");Node node2 = new Node(2, "2");Node node3 = new Node(3, "3");Node node4 = new Node(4, "4");Node node5 = new Node(5, "5");root1.setLeft(node2);root1.setRight(node3);node3.setLeft(node5);node3.setRight(node4);BinaryTree binaryTree = new BinaryTree();binaryTree.setRoot(root1);binaryTree.preOrder();//1,2,3,5,4System.out.println();
//        binaryTree.infixOrder();//2,1,5,3,4
//        System.out.println();
//        binaryTree.postOrder();//2,5,4,3,1
//        binaryTree.preOrderSearch(2);}
}//定义二叉树
class BinaryTree {private Node root;public Node getRoot() {return root;}public void setRoot(Node root) {this.root = root;}//前序public void preOrder() {if (this.root != null) {this.root.preOrder();} else {System.out.println("二叉树为空~~");}}//中public void infixOrder() {if (this.root != null) {this.root.infixOrder();} else {System.out.println("二叉树为空~~");}}//后public void postOrder() {if (this.root != null) {this.root.postOrder();} else {System.out.println("二叉树为空~~");}}//前序查找public void preOrderSearch(int no) {Node resnode = null;if (this.root != null) {resnode = this.root.preOrderSearch(no);}if (resnode != null) {System.out.println("查找结果-->" + resnode);} else {System.out.println("find nothings");}}
}//创建结点
class Node {private int no;private String name;private Node left; //默认是nullprivate Node right; //默认是nullpublic Node(int no, String name) {this.no = no;this.name = name;}public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}public Node getLeft() {return left;}public void setLeft(Node left) {this.left = left;}public Node getRight() {return right;}public void setRight(Node right) {this.right = right;}@Overridepublic String toString() {return "Node{" +"no=" + no +", name='" + name + '\'' +'}';}//编写前序遍历的方法public void preOrder() {System.out.println(this);if (this.left != null) {this.left.preOrder();}if (this.right != null) {this.right.preOrder();}}//中序遍历public void infixOrder() {if (this.left != null) {this.left.infixOrder();}System.out.println(this);if (this.right != null) {this.right.infixOrder();}}//后序遍历public void postOrder() {if (this.left != null) {this.left.postOrder();}if (this.right != null) {this.right.postOrder();}System.out.println(this);}//前序遍历查找public Node preOrderSearch(int no) {if (this.no == no) {return this;}Node resNode = null;if (this.left != null) {resNode = this.left.preOrderSearch(no);}if (resNode != null) {return resNode;}if (this.right != null) {resNode = this.right.preOrderSearch(no);}return resNode;}
}

3.删除结点

叶子节点,删除结点;
非叶子结点,删除子树

思路:
因为二叉树是单向的,所以判断当前节点的子节点是否是需要删除的结点(而不是判断当前节点是不是需要删除的结点
i 针对左子节点(非空且是待删除),this.left = null;右子节点同理;
ii 如果没有删除结点,则左/右子树递归
iii 在节点类里面加入删除方法,没考虑根节点,要在二叉树类里面单独写一段判断root是否是待删除的条件

上代码:
节点类中的删除方法:

    //删除结点public void delNode(int no) {if (this.left != null && this.left.no == no) {//左子结点是待删除结点this.left = null;return;}if (this.right != null && this.right.no == no) {//右子节点 是待删除结点this.right = null;return;}//如果左右子结点都不是待删除结点,遍历左右子树if (this.left != null) {this.left.delNode(no);}if (this.right != null) {this.right.delNode(no);}}

二叉树类中的方法:

    //删除结点public void delNode(int no) {if (root != null && root.getNo() == no) {//判断root结点是否是待删除结点(节点类的删除没把root包含进去)root = null;}root.delNode(no);}

4.顺序存储二叉树

概念: 数组可以转化成树, 数可以转换成数组
在这里插入图片描述

顺序存储二叉树特点: (n是数组下标 对应二叉树的下标
i 通常只考虑完全二叉树
ii 第n个元素的左子节点:2*n+1 ;右子节点:2*n+2
iii 第n个元素是父节点: (n-1)/2

上代码:

public class ArrBinaryTreeDemo {@Testpublic void test() {int[] arr = {1, 2, 3, 4, 5, 6, 7};ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);arrBinaryTree.preOrder();//1,2,4,5,3,6,7}
}class ArrBinaryTree {private int[] arr;public ArrBinaryTree(int[] arr) {this.arr = arr;}//方法重载,(针对第一步下标是0的,public void preOrder(){preOrder(0);}/*** 顺序存储-前序遍历** @param index 数组下标*/public void preOrder(int index) {if (arr == null || arr.length == 0) {System.out.println("数组为空,不能进行二叉树的前序遍历");return;}//输出当前的元素System.out.println(arr[index]);//左递归if ((index * 2 + 1) < arr.length) {preOrder((index * 2 + 1));}//右递归if ((index * 2 + 2) < arr.length) {preOrder((index * 2 + 2));}}
}

个人小结:
顺序存储二叉树,并没有实际创建结点. 而是先接受数组,按照二叉树的遍历方式输出.

5.线索化二叉树

引:二叉树的叶子结点,存在指针的浪费

基本介绍:
n个结点的二叉树有 n+1 个空指针域
利用二叉树的空指针域指向该结点的在某次遍历次序下的前驱或者后继结点的指针,叫做线索化二叉树(分,前中后序线索化二叉树)
如图:
在这里插入图片描述

实现思路:
线索化二叉树后,node结点的left和right存在两种情况:
i left可能指向左子树,也可能指向前驱
ii rigth…
传入一个node结点,定义一个pre结点(指向的是node的前驱)
如果传入的node的left是空,则让他指向前驱;同时每次线索化一个节点后,让pre指向node(记录下一个传入node参数的前驱结点),以此通过pre操作后继结点的指向!

上核心代码:
ndoe结点类:(在原来的基础上,加上了leftTyperightType,用来一个结点的left/right是指向左子树还是前驱/…
binaryTree创建二叉树类:(在原来的基础上,加入前驱结点pre,加入线索化的方法

//定义二叉树
class BinaryTree {//中序遍历....//前驱节点 !!! 注意每次线索化结点后更新...private Node pre = null;//线索化二叉树--中序public void threadedNodes(Node node) {if (node == null) {//不能线索化return;}//(一)线索化左子树threadedNodes(node.getLeft());//(二)线索化当前结点 -- 中序结果:(8,3,10,1,14,6)//处理当前节点的前驱节点 (以8 为例 8.left->null, 8.leftType = 1(前驱)if (node.getLeft() == null) {//让当前结点的左指针指向前驱节点node.setLeft(pre);//修改当前节点的左指针类型为1(指向的前驱节点)node.setLeftType(1);}//处理后继节点if (pre != null && pre.getRight() == null) {pre.setRight(node);pre.setRightType(1);}//处理每个结点后,让当前结点是下一个结点的前驱节点pre = node;//(三)线索化右子树threadedNodes(node.getRight());}//线索化重载public void threadedNodes(){threadedNodes(root);}
}
//结点类  :在基础上增加一个判断线索化相关的变量
class Node {....//中序线索化二叉树 说明:// --如果leftType/rightType为 0,表示指向左/右子树;为 1,则表示指向 前驱/后继结点private int leftType;private int rightType;//中序遍历........
}

编程小结:
1.线索化二叉树,判断是否是叶子结点,很关键
2.每次线索化一个结点后,一定要把他设置为pre;因为需要通过pre设置后继节点
另外,关于线索化画出连线:只看叶子结点,根据中序遍历结果查看10,14等,可以发现和1的画线关系(只看叶子结点!!) 如下图:
在这里插入图片描述

6.遍历线索化二叉树

1.分析:
因为线索化之后,各个节点的指向有变化的,原来的遍历方法(判断左右子节点是否是空)不能使用了. 需要新的遍历方式遍历线索化二叉树,各个节点通过线性的方式遍历,提高了遍历的效率.

2.上代码:
思路: – 循环
i 从根结开始找到第一个 leftType == 1
ii 打印
iii 当前结点的右指针指向的是 后继节点(rightType==1 ),就一直输出
iiii 更新这次遍历的结点 (tempNode = tempNode.getRight();)

//线索化遍历二叉树 -- 中序(8,3,10,1,14,6)
public void threadedList() {Node tempNode = root;//从root开始遍历while (tempNode != null) {//循环,找到第一个为leftType==1的 -->8while (tempNode.getLeftType() == 0) {tempNode = tempNode.getLeft();}//打印System.out.println(tempNode);while (tempNode.getRightType() == 1) {//如果当前结点的右指针指向的是 后继节点,就一直输出tempNode = tempNode.getRight();System.out.println(tempNode);}//替换这个遍历的结点tempNode = tempNode.getRight();}
}

相关内容

热门资讯

埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
应用未安装解决办法 平板应用未... ---IT小技术,每天Get一个小技能!一、前言描述苹果IPad2居然不能安装怎么办?与此IPad不...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...
脚上的穴位图 脚面经络图对应的... 人体穴位作用图解大全更清晰直观的标注了各个人体穴位的作用,包括头部穴位图、胸部穴位图、背部穴位图、胳...
猫咪吃了塑料袋怎么办 猫咪误食... 你知道吗?塑料袋放久了会长猫哦!要说猫咪对塑料袋的喜爱程度完完全全可以媲美纸箱家里只要一有塑料袋的响...
demo什么意思 demo版本... 618快到了,各位的小金库大概也在准备开闸放水了吧。没有小金库的,也该向老婆撒娇卖萌服个软了,一切只...
世界上最漂亮的人 世界上最漂亮... 此前在某网上,选出了全球265万颜值姣好的女性。从这些数量庞大的女性群体中,人们投票选出了心目中最美...
埃菲尔铁塔在哪 中国仿建埃菲尔... 2019年4月26日,广西南宁市,街头惊现一座巨型山寨版埃菲尔铁塔,高约20米,白色塔身,造型逼真,...
苗族的传统节日 贵州苗族节日有... 【岜沙苗族芦笙节】岜沙,苗语叫“分送”,距从江县城7.5公里,是世界上最崇拜树木并以树为神的枪手部落...
北京的名胜古迹 北京最著名的景... 北京从元代开始,逐渐走上帝国首都的道路,先是成为大辽朝五大首都之一的南京城,随着金灭辽,金代从海陵王...
长白山自助游攻略 吉林长白山游... 昨天介绍了西坡的景点详细请看链接:一个人的旅行,据说能看到长白山天池全凭运气,您的运气如何?今日介绍...