ArrayList底层其实也是扩容。
数组:检索读取的效率还可以,但是插入删除的效率偏低。
链表:插入删除的效率好。但是检索读取的效率偏低。
所以引入二叉树。特别是二叉排序树、红黑树(HashMap的底层实现)。
对数据的增删改查的速度都会提高。
小概念
结点
根节点
父节点
子节点
叶子结点
结点的权(节点的值)
层
满二叉树
完全二叉树
一般来说我们只说二叉树。
前序遍历
中序遍历
后序遍历
前序:根左右
中序:左根右
后序:左右根
package BinaryTree;
/**
* @author pdzz
* @create 2019-11-19 10:29
*/
class Node{
private int no;
private String name;
private Node left;
private Node right;
public 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;
}
@Override
public 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 midOrder(){
//递归向左子树遍历
if (this.left != null){
this.left.midOrder();
}
//输出当前结点
System.out.println(this);
//向右递归当前结点
if (this.right != null){
this.right.midOrder();
}
}
//后序遍历
public void postOrder(){
if (this.left != null){
this.left.postOrder();
}
if (this.right != null){
this.right.postOrder();
}
System.out.println(this);
}
}
//定义一个二叉树
class BinaryTree{
private Node 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 midOrder(){
if (this.root != null){
this.root.midOrder();
}else {
System.out.println("二叉树为空无法遍历!");
}
}
public void postOrder(){
if (this.root != null){
this.root.postOrder();
}else {
System.out.println("二叉树为空无法遍历!");
}
}
}
public class BinaryTreeDemo {
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
Node root = new Node(1,"a");
Node node2 = new Node(2,"b");
Node node3 = new Node(3,"c");
Node node4 = new Node(4,"d");
root.setLeft(node2);
root.setRight(node3);
node3.setRight(node4);
binaryTree.setRoot(root);
/*
* 1
* 2 3
* 4
*
* 可以看出:前序遍历就是:1-2-3-4
* 中序遍历就是:2-1-3-4
* 后序遍历就是:2-4-3-1
*
* 1
* 2 3
* 5 4
*
*前序遍历:1-2-3-5-4
*中序遍历:2-1-5-3-4
*后序遍历:2-5-4-3-1
*
* */
binaryTree.preOrder();
binaryTree.midOrder();
binaryTree.postOrder();
}
}
前中后序查找指定节点
public Node preOrderSearch(int id){
//前序遍历先找根节点
if (this.no == id){
return this;
}
Node resNode = null;
if (this.left != null){
resNode = this.left.preOrderSearch(id);
}
if (resNode != null){
return resNode;
}
if (this.right != null){
resNode = this.right.preOrderSearch(id);
}
return resNode;
}
public Node midOrderSearch(int id){
Node resNode = null;
if (this.left != null){
resNode = this.left.midOrderSearch(id);
}
if (resNode != null){
return resNode;
}
if (this.no == id){
return this;
}
if (this.right != null){
resNode = this.right.midOrderSearch(id);
}
return resNode;
}
public Node postOrderSearch(int id){
Node resNode = null;
if (this.left != null){
resNode = this.left.postOrderSearch(id);
}
if (resNode != null){
return resNode;
}
if (this.right != null){
resNode = this.right.postOrderSearch(id);
}
if (this.no == id){
return this;
}
return resNode;
}
BinaryTree类中的代码:
//前序遍历
public Node preOrderSearch(int id){
if (root != null){
return root.preOrderSearch(id);
}else {
System.out.println("找不到id为" + id + "的节点");
return null;
}
}
public Node midOrderSearch(int id){
if (root != null){
return root.midOrderSearch(id);
}else {
System.out.println("找不到id为" + id + "的节点");
return null;
}
}
public Node postOrderSearch(int id){
if (root != null){
return root.postOrderSearch(id);
}else {
System.out.println("找不到id为" + id + "的节点");
return null;
}
}
删除节点
public void delNode(int id){
if (root != null){
if (root.getNo() == id){
root = null;
return;
}
else{
root.delNode(id);
}
}else {
System.out.println("树是空树");
}
}
public void delNode(int id){
if (this.left != null && this.left.no == id){
this.left = null;
return;
}
if (this.right != null && this.right.no == id){
this.right = null;
return;
}
if (this.left != null){
this.left.delNode(id);
}
if (this.right != null){
this.right.delNode(id);
}
}
线索二叉树
class ThreadedBinaryTree {
private HeroNode root;
//为了实现线索化,需要创建要给指向当前结点的前驱结点的指针
//在递归进行线索化时,pre 总是保留前一个结点
private HeroNode pre = null;
public void setRoot(HeroNode root) {
this.root = root;
}
//重载一把threadedNodes方法
public void threadedNodes() {
this.threadedNodes(root);
}
//遍历线索化二叉树的方法
public void threadedList() {
//定义一个变量,存储当前遍历的结点,从root开始
HeroNode node = root;
while(node != null) {
//循环的找到leftType == 1的结点,第一个找到就是8结点
//后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
//处理后的有效结点
while(node.getLeftType() == 0) {
node = node.getLeft();
}
//打印当前这个结点
System.out.println(node);
//如果当前结点的右指针指向的是后继结点,就一直输出
while(node.getRightType() == 1) {
//获取到当前结点的后继结点
node = node.getRight();
System.out.println(node);
}
//替换这个遍历的结点,例如就是3输出以后,3的getRightType不等于1
//所以退出while循环,执行下面的node = node.getRight();就是将当前的
//node(值为3)替换为其 右子节点10,实现循环的操作。10的话左子节点的
//类型不为1,所以打印出来10,继续判断其右子节点的类型是否为1,显
//然是1,所以继续打印10的后继节点1,然后继续循环,1的右子节点的
//类型不为1,所以退出循环,再次让当前的node(值为1)的指针指向的它的
//右指针(值为6)。
node = node.getRight();
}
}
//编写对二叉树进行中序线索化的方法
/**
*
* @param node 就是当前需要线索化的结点
*/
public void threadedNodes(HeroNode node) {
//如果node==null, 不能线索化
if(node == null) {
return;
}
//(一)先线索化左子树
threadedNodes(node.getLeft());
//(二)线索化当前结点[有难度]
//处理当前结点的前驱结点
//以8结点来理解
//8结点的.left = null , 8结点的.leftType = 1
if(node.getLeft() == null) {
//让当前结点的左指针指向前驱结点
node.setLeft(pre);
//修改当前结点的左指针的类型,指向前驱结点
node.setLeftType(1);
}
//处理后继结点
if (pre != null && pre.getRight() == null) {
//让前驱结点的右指针指向当前结点
pre.setRight(node);
//修改前驱结点的右指针类型
pre.setRightType(1);
}
//!!! 每处理一个结点后,让当前结点是下一个结点的前驱结点
pre = node;
//(三)在线索化右子树
threadedNodes(node.getRight());
}
集合的源码仔细看。
二叉树的遍历
图的深度优先遍历DFS