一、二叉树
在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
一棵深度为k,且有2^k-1个结点的二叉树,称为满二叉树。这种树的特点是每一层上的结点数都是最大结点数。而在一棵二叉树中,除最后一层外,若其余层都是满的,并且或者最后一层是满的,或者是在右边缺少连续若干结点,则此二叉树为完全二叉树。具有n个结点的完全二叉树的深度为floor(log2n)+1。深度为k的完全二叉树,至少有2k-1个叶子结点,至多有2k-1个结点。
二、二叉树特点
- 在非空二叉树中,第i层的结点总数不超过 2^(i-1)个结点;
- 深度为h的二叉树最多有2^h -1 个结点(h=1),最少有h个结点;
- 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
- 具有n个节点的完全二叉树的深度为[log2n]+1,其中[log2n]+1是向下取整
- 若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i的结点:
(1) 若 i=1,则该结点是二叉树的根,无双亲, 否则,编号为 [i/2] 的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子, 否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点, 否则,编号为2i+1 的结点为其右孩子结点。
三、二叉树的Java实现:
public class BinaryTree {
private Node root;//根结点
public BinaryTree() {
this.root = null;
}
//插入
public void insert(String data) {
Node dataNode = new Node(data);
//判断根节点为kong就返回
if (root == null) {
root = dataNode;
} else {
Node currentNode = root;
while (true) {
//如果大于根节点则放在根节点的右边
if (Integer.valueOf(dataNode.data) > Integer.valueOf(currentNode.data)) {
Node rightChild = currentNode.rightChild;
//如果右子节点为空则设置为右子节点并且返回
if (rightChild == null) {
currentNode.rightChild = dataNode;
return;
}
//如果没找到则继续比较右子节点赋值为当前节点重复操作
currentNode = currentNode.rightChild;
//如果小于根节点则放在根节点的右边
} else {
Node leftChild = currentNode.leftChild;
//如果右子节点为空则设置为左子节点
if (leftChild == null) {
currentNode.leftChild = dataNode;
return;
}
//如果没找到则继续比较左子节点赋值为当前节点重复操作
currentNode = currentNode.leftChild;
}
}
}
}
public Node find(String data) {
if (root == null) {
return null;
}
Node cur = root;
//只要当前节点的值和data不相等则继续循环递归找
while (!cur.data.equals(data)) {
//大于则把当前的右子节点赋值为当前节点
if (Integer.valueOf(data) > Integer.valueOf(cur.data)) {
cur = cur.rightChild;
//否则把当前的左子节点赋值为当前节点
} else {
cur = cur.leftChild;
}
//如果当前节点为空则没有找到
if (cur == null) {
return null;
}
}
return cur;
}
public boolean delete(String data) {
//删除分3种情况,1.删除节点为叶子节点 2.删除的节点只有一个节点 3.删除的有两种节点
//第三种情况需要寻找后继节点,后继节点就是比要删除的节点的关键值要大的节点集合中的最小值。(右子节点的左后代)
if (root ==null){
return false;
}
boolean isLeftChild = true;
//首先需要找到是否存在这个节点不存在就直接返回false,并且确定他是左子树还是右子树
Node cur = root; //要删除的节点
Node parent = null;//要删除节点的父节点
while (!cur.data.equals(data)) {
parent = cur;
//大于则把当前的右子节点赋值为当前节点
if (Integer.valueOf(data) > Integer.valueOf(cur.data)) {
cur = cur.rightChild;
isLeftChild = false;
//否则把当前的左子节点赋值为当前节点
} else {
isLeftChild = true;
cur = cur.leftChild;
}
//如果当前节点为空则没有找到
if (cur == null) {
return false;
}
}
//判断叶子节点
if (cur.leftChild==null && cur.rightChild==null){
if (root.data.equals(cur.data)){
root = null;
}else if (isLeftChild){
parent.leftChild = null;
}else {
parent.rightChild = null;
}
//只有一个右节点
}else if (cur.leftChild==null){
//如果是根节点直接删除,把当前的右节点赋值给root
if (root.data.equals(cur.data)){
root = cur.rightChild;
//如果当前需要删除的节点是左孩子则需要将当前节点的右子节点赋值给当前节点父节点的左孩子节点
}else if(isLeftChild){
parent.leftChild = cur.rightChild;
}else {
//如果当前需要删除的节点是右孩子则需要将当前节点的右子节点赋值给当前节点父节点的右孩子节点
parent.rightChild = cur.rightChild;
}
//只有一个左节点
}else if (cur.rightChild == null){
//如果是根节点直接删除,把当前的左节点赋值给root
if (root.data.equals(cur.data)){
root = cur.leftChild;
//如果当前需要删除的节点是左孩子则需要将当前节点的左子节点赋值给当前节点父节点的左孩子节点
}else if (isLeftChild){
parent.leftChild = cur.leftChild;
}else {
//如果当前需要删除的节点是右孩子则需要将当前节点的左子节点赋值给当前节点父节点的右孩子节点
parent.rightChild = cur.leftChild;
}
}else {
//删除节点有两个孩子
//找到当前节点的后继节点
Node successor = getSuccessor(cur);
if(cur == root){
root = successor;
}else if(isLeftChild){
parent.leftChild = successor;
}else {
parent.rightChild = successor;
}
successor.leftChild = cur.leftChild;
}
return true;
}
/**
* 获取要删除节点的中序后继节点
* @param delNode
* @return
*/
public Node getSuccessor(Node delNode) {
Node successor = delNode;
Node successorParent = delNode;
Node curr = delNode.rightChild;
while(curr != null){
successorParent = successor;
successor = curr;
curr = curr.leftChild;
//左子节点不为空则一直找下去
}
if(successor != delNode.rightChild){
//后继节点和删除的节点不相等
successorParent.leftChild = successor.rightChild;
successor.rightChild = delNode.rightChild;
}
return successor;
}
//遍历分为前序遍历中序遍历后序遍历都是相对于根节点
//前序遍历
public void frontOrder(Node root){
if (root!=null){
//前序根节点的值在前面
System.out.println(root.data+" ");
frontOrder(root.leftChild);
frontOrder(root.rightChild);
}
}
//中序遍历
public void middleOrder(Node root){
if (root!=null) {
middleOrder(root.leftChild);
//中序根节点的值在中间
System.out.println(root.data + " ");
middleOrder(root.rightChild);
}
}
//中序遍历
public void lastOrder(Node root){
if (root!=null) {
middleOrder(root.leftChild);
middleOrder(root.rightChild);
//后序序根节点的值在后面
System.out.println(root.data + " ");
}
}
//节点
static class Node {
private String data;//结点值
private Node leftChild; //左子结点
private Node rightChild;//右子结点
public Node(String data) {
this.data = data;
}
}
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
binaryTree.insert("1");
binaryTree.insert("3");
binaryTree.insert("5");
binaryTree.insert("8");
binaryTree.insert("10");
binaryTree.insert("19");
binaryTree.insert("12");
binaryTree.insert("17");
binaryTree.frontOrder(binaryTree.root);
binaryTree.middleOrder(binaryTree.root);
binaryTree.lastOrder(binaryTree.root);
}
}