一、基本概念
- 二叉树:每个节点最多有两个子树的结构;
- 满二叉树:除了最后一层没有任何节点外,每一层的所有节点都有两个子节点的二叉树;
- 完全二叉树:结构与满二叉树类似,不同点在于最后一层可以不满,但最后一层的节点必须连续集中再最左边;
- 二叉搜索树:各节点的值有大小要求的二叉树,左节点的值<中节点的值<右节点的值。
二、树的存储方式
1、数组形式
使用数组存储,有一个特点就是必须连续存储,根据树的结构,满二叉树和完全二叉树可以对树的元素从上到下,从左到右进行连续存储,但是对于一般的二叉树,必须进行完全化才可以进行数组存储。所谓的完全化,就是对于没有子节点的位置,使用null代替,构成完全二叉树的结构。那么问题就来了,使用null存储,也是会占用存储空间的,所以对树的存储方式,一般采取链表的形式。
2、链表形式
链表形式可以分为两类:二叉链表和三叉链表。
二叉链表存储,包含三个信息,左孩子节点指针,右孩子节点指针,以及数据域。如果没有孩子节点,则将指针域置为NULL。使用二叉链表结构,一般会创建一个头节点,并将其中的左子树指向根节点,右子树为NULL,这样方便了后续的操作。
三叉链表存储,除了含有二叉链表的结构外,还含有一个双亲节点指针域,指向它的双亲节点。这是典型的空间换时间的方法,如果涉及的操作需要频繁访问双亲节点,就建议使用三叉链表的存储结构。
对于一般的二叉树而言,二叉链表存储是最简单有效的方式,也是推荐使用的。
三、树的实现
package com.cjs.bean;
public class Node {
private int id;
private int data;
private Node leftChild;
private Node rightChild;
public Node() {
}
public Node(int id, int data, Node leftChild, Node rightChild) {
this.id = id;
this.data = data;
this.leftChild = leftChild;
this.rightChild = rightChild;
}
public Node(int id, int data) {
this.id = id;
this.data = data;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Node getLeftChild() {
return leftChild;
}
public void setLeftChild(Node leftChild) {
this.leftChild = leftChild;
}
public Node getRightChild() {
return rightChild;
}
public void setRightChild(Node rightChild) {
this.rightChild = rightChild;
}
}
package com.cjs;
import com.cjs.bean.Node;
public class BinaryTree {
private Node root;
/**
* 查找节点
* @param key 要查找的属性id值
* @return
*/
public Node find(int key) {
Node current = root;
while (current!=null) {
if (key > current.getId()) {
current = root.getRightChild();
} else if (key < current.getId()) {
current = root.getLeftChild();
} else if (key == current.getId()) {
return current;
}
}
return current;
}
/**
* 插入一个节点
* @param id
* @param data
*/
public void insert(int id, int data) {
Node newNode = new Node(id,data);
if (root == null) {
root = newNode;
} else {
Node current = root;
//定义parent节点,如果插入的节点不是root,肯定会影响到parent节点
Node parent = null;
while (true) {
parent = current;
if (id < current.getId()) {
current = current.getLeftChild();
if (current == null) {
//如果当前节点为空,说明已经找到了。
parent.setLeftChild(newNode);
return;
}
} else if (id > current.getId()) {
current = current.getRightChild();
if (current == null) {
parent.setRightChild(newNode);
return;
}
}
}
}
}
/**
* 从node节点开始前序遍历
* @param node
*/
public void preOrder(Node node) {
if (node!=null) {
System.out.println(node.getData() + ",");
preOrder(node.getLeftChild());
preOrder(node.getRightChild());
}
}
/**
* 从node节点开始中序遍历
* @param node
*/
public void inOrder(Node node) {
if (node!=null) {
preOrder(node.getLeftChild());
System.out.println(node.getData() + ",");
preOrder(node.getRightChild());
}
}
/**
* 从node节点开始后序遍历
* @param node
*/
public void postOrder(Node node) {
if (node!=null) {
preOrder(node.getLeftChild());
preOrder(node.getRightChild());
System.out.println(node.getData() + ",");
}
}
/**
* 获取最小节点,树的最左边为最小节点
* @return
*/
public Node getMinNode() {
Node current = root;
Node lastNode = null;
while (current != null) {
lastNode = current;
current = current.getLeftChild();
}
return lastNode;
}
/**
* 获取最大节点,树的最右边为最大节点
* @return
*/
public Node getMaxNode() {
Node current = root;
Node lastNode = null;
while (current != null) {
lastNode = current;
current = current.getRightChild();
}
return lastNode;
}
public boolean delete(int key) {
Node current = root;
Node parent = root;
boolean isLeftChild = true;
while (current!=null) {
parent = current;
if (key > current.getId()) {
isLeftChild = false;
current = root.getRightChild();
} else if (key < current.getId()) {
isLeftChild = true;
current = root.getLeftChild();
}
if (key == current.getId()) {
break;
}
}
// 没有找到需要删除的节点
if (current == null) {
return false;
}
if (current.getLeftChild() == null && current.getRightChild() == null) {
// 1、需要删除的节点没有子节点,直接置为null
if (current == root) {
root = null;
}
else {
this.setNode(parent, null, isLeftChild);
}
}
else if (current.getRightChild() == null) {
// 2、需要删除的节点只有一个子节点,将该子节点直接替代
this.setNode(parent, current.getLeftChild(), isLeftChild);
}
else {
// 3、需要删除的节点有两个子节点,查找右子树的最左边节点,作为替代节点
Node minNode = current.getRightChild();
Node minNodeParent = minNode;
while (minNode.getLeftChild()!=null) {
minNodeParent = minNode;
minNode = minNode.getLeftChild();
}
this.setNode(parent, minNode, isLeftChild);
minNode.setLeftChild(current.getLeftChild());
if (minNodeParent!=minNode) {
minNodeParent.setLeftChild(null);
minNode.setRightChild(current.getRightChild());
}
}
return true;
}
/**
* 设置替代节点的值
* @param parent
* @param targetNode
* @param isLeftChild
*/
private void setNode(Node parent, Node targetNode, boolean isLeftChild) {
if (isLeftChild) {
parent.setLeftChild(targetNode);
} else {
parent.setRightChild(targetNode);
}
}
}