版权声明:转载请注明原文地址。 https://blog.csdn.net/qq_39240270/article/details/86847613
什么是二叉树
在计算机科学中,二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)。二叉树常被用于实现二叉查找树和二叉堆。
一棵深度为k,且有2^k-1个节点的二叉树,称为满二叉树。这种树的特点是每一层上的节点数都是最大节点数。而在一棵二叉树中,除最后一层外,若其余层都是满的,并且最后一层或者是满的,或者是在右边缺少连续若干节点,则此二叉树为完全二叉树。具有n个节点的完全二叉树的深度为floor(log2n)+1。深度为k的完全二叉树,至少有2k-1个叶子节点,至多有2k-1个节点。
二叉树的应用
我们知道,在有序数组中,可以快速找到特定的值,但是想在有序数组中插入一个新的数据,就必须首先找出新数据项插入的位置,然后将比新数据大的数据向后移动一位,来给新的数据腾出空间,删除同理,这样移动非常的费时。显而易见,如果要做很多的插入和删除操作,就不该选用有序数组。
链表中可以快速添加和删除某个数据,但是在链表中查找数据必须从头开始访问链表的每一个数据,知道找到该数据为止,这个过程很慢。
树这种数据结构,既能像链表那样快速的插入和删除,又能像有序数组那样快速查找。二叉搜索树有如下特点:一个节点的左子节点的值小于这个节点,右子节点的值大于或等于这个节点。插入一个节点需要根据这个规则进行插入。
删除节点时二叉搜索树中最复杂的操作,但是删除节点在很多树的应用中又非常重要,所以详细研究并总结以下特点。删除节点时,首先找到节点,这个要删除的节点可能有三种情况需要考虑:
- 该节点是叶节点,没有子节点(简单)
- 该节点有一个子节点(比较简单)
- 该节点有两个子节点(复杂)
- 要删除的节点是叶节点,没有子节点:
要删除叶节点,只需要改变该节点的父节点对应子节点的值即可,由指向该叶节点改为null就可以了。垃圾回收器会自动回收该叶节点,不需要手动删掉。 - 要删除的节点只有一个子节点:
当节点有一个子节点时,这个节点只有两个连接:连向父节点和连向它唯一的子节点。需要从这个序列中剪断这个节点,把它的子节点直接连到它的父节点上即可,这个过程要求改变父节点适当的引用(左子节点还是右子节点),指向要删除节点的子节点即可。 - 要删除的节点有两个子节点:
这种情况最复杂,如果要删除有两个子节点的节点,就不能只用它的一个子节点代替它,因为它的子节点本身还可能有子节点,简单的用子节点代替不能保证二叉树的有序性。所以要用另一种办法,利用要删除节点的中序后继来代替该节点。那么如何找后继节点呢?首先得找到要删除的节点的右子节点,它的关键字值一定比待删除节点的大。然后转到待删除节点右子节点的左子节点那里(如果有的话),然后到这个左子节点的左子节点,以此类推,顺着左子节点的路径一直向下找,这个路径上的最后一个左子节点就是待删除节点的后继。如果待删除节点的右子节点没有左子节点,那么这个右子节点本身就是后继。
找到中序后继后,就要开始删除了,如果后继节点不是待删除节点的右子节点,要执行四个步骤:
- 把后继父节点的leftChild节点置为后继的右子节点;
- 把后继的rightChild节点置为要删除节点的右子节点;
- 把待删除节点从它父节点的leftChild或rightChild节点删除,把这个字段置为后继;
- 把待删除的左子节点移除,将后继的leftChild字段置为待删除节点的左子节点。
如果后继节点就是待删除节点的右子节点,这种情况就比较简单了,此时只需要把后继为根的子树移到删除的节点的位置即可。
利用链表实现二叉树
功能如下:
- 插入节点
- 查找某个特点值的节点
- 递归实现三种遍历:
1. 前序遍历
2. 中序遍历
3. 后序遍历 - 查找最小的节点,即树最’左’的节点
- 查找最大的节点,即树最’右’的节点
- 删除某个特定值的节点
代码如下:
package binaryTree;
public class BinaryTree {
public Node root;//根节点
public BinaryTree(){
root = null;
}
//查找某个特定值的节点
public Node findNode(int data){
//临时节点用于查找
Node current = root;
if(root == null){
return null;
}
while(data != current.data){
if(data < current.data){
current = current.leftChild;
}else{
current = current.rightChild;
}
if(current == null){
System.out.println("没有值为" + data + "的节点");
return null;
}
}
return current;
}
//插入节点
public void insert(int data){
//创建新节点
Node newNode = new Node(data);
//创建要插入节点的父节点以及要插入节点的位置
Node current = root;
Node parent;
if(root == null){
root = newNode;
}else{
while(true){
parent = current;
if(data < current.data){
current = current.leftChild;
if(current == null){
parent.leftChild = newNode;
newNode.parent = parent;
return;
}
}else{
current = current.rightChild;
if(current == null){
parent.rightChild = newNode;
newNode.parent = parent;
return;
}
}
}
}
}
//递归实现遍历二叉树
public void traverse(int traverseOrder){
switch(traverseOrder){
case 1:System.out.println("前序遍历");
preTraverse(root);
break;
case 2:System.out.println("中序遍历");
minTraverse(root);
break;
case 3:System.out.println("后序遍历");
backTraverse(root);
break;
default:System.out.println("前序遍历");
preTraverse(root);
break;
}
}
//前序遍历
public void preTraverse(Node node){
if(node != null){
System.out.println(node.data);
preTraverse(node.leftChild);
preTraverse(node.rightChild);
}
}
//中序遍历
public void minTraverse(Node node){
if(node != null){
minTraverse(node.leftChild);
System.out.println(node.data);
minTraverse(node.rightChild);
}
}
//后序遍历
public void backTraverse(Node node){
if(node != null){
backTraverse(node.leftChild);
backTraverse(node.rightChild);
System.out.println(node.data);
}
}
//查找最小的元素,即树最'左'的节点
public Node findMin(){
Node parent = root;
Node current = root;
if(root == null){
return null;
}else{
while(current != null){
parent = current;
current = current.leftChild;
}
}
return parent;
}
//查找最大的元素,即树最'右'的节点
public Node findMax(){
Node parent = root;
Node current = root;
if(root == null){
return null;
}else{
while(current != null){
parent = current;
current = current.rightChild;
}
}
return parent;
}
/*
* 删除节点,要分三种情况
* 要删除的节点没有子节点(简单)
* 要删除的节点只有一个子节点(略复杂)
* 要删除的节点有两个子节点(复杂)
*/
public boolean delete(int data){
//创建临时节点用于找到要删除的节点
Node current = root;
//判断该节点是左节点还是右节点
boolean isLeftChild =true;
while(current.data != data){
if(data < current.data){
current = current.leftChild;
}else{
current = current.rightChild;
}
if(current == null){
System.out.println("没有值为" + data + "的节点");
return false;
}
}
//找到了要被删除的节点
if(current.leftChild == null && current.rightChild ==null){
//要删除的节点没有子节点
return deleteNoChild(current,isLeftChild);
}else if(current.leftChild != null && current.rightChild != null){
//要删除的节点有两个子节点
return deleteTwoChild(current,isLeftChild);
}else{
//要删除的节点只有一个子节点
return deleteOneChild(current,isLeftChild);
}
}
//要删除的节点没有子节点
public boolean deleteNoChild(Node delNode,boolean isLeftChild){
if(delNode == root){
//要删除的节点是根节点
root = null;
return true;
}else{
if(isLeftChild){
//要删除的节点是左节点
delNode.parent.leftChild = null;
return true;
}else{
//要删除的节点是右节点
delNode.parent.rightChild = null;
return true;
}
}
}
//要删除的节点有一个子节点
public boolean deleteOneChild(Node delNode,boolean isLeftChild){
if(delNode.leftChild == null){
//要删除的节点只有右子节点
//要删除的节点为根节点
if(delNode == root){
root = delNode.rightChild;
delNode.rightChild.parent = null;
return true;
}
if(isLeftChild){
//要删除的节点是左子结点
delNode.parent.leftChild = delNode.rightChild;
}else{
//要删除的节点是右子节点
delNode.parent.rightChild = delNode.rightChild;
}
delNode.rightChild.parent = delNode.parent;
return true;
}else{
//要删除的节点只有左子结点
//要删除的节点为根节点
if(delNode == root){
root = delNode.leftChild;
delNode.leftChild.parent = null;
}
if(isLeftChild){
//要删除的节点是左子结点
delNode.parent.leftChild = delNode.leftChild;
}else{
//要删除的节点是右子节点
delNode.parent.rightChild = delNode.leftChild;
}
delNode.leftChild.parent = delNode.parent;
return true;
}
}
//要删除的节点有两个子节点
public boolean deleteTwoChild(Node delNode,boolean isLeftChild){
//得到要删除节点的后继节点
Node successor = getSuccessor(delNode);
//要删除的节点是根节点
if(delNode == root){
//将后继节点与被删除的节点的左边连起来
delNode.leftChild.parent = successor;
successor.leftChild = delNode.leftChild;
root = successor;
return true;
}else{
//要删除的节点是左子结点
if(isLeftChild){
//将后继节点与被删除的节点的左边连起来
delNode.leftChild.parent = successor;
successor.leftChild = delNode.leftChild;
//将后继节点与被删除的节点的父节点连起来
delNode.leftChild = successor;
successor.parent = delNode.parent;
return true;
}else{
//要删除的节点是右子节点
//将后继节点与被删除的节点的左边连起来
delNode.leftChild.parent = successor;
successor.leftChild = delNode.leftChild;
//将后继节点与被删除的节点的父节点连起来
delNode.rightChild = successor;
successor.parent = delNode.parent;
return true;
}
}
}
//获取要删除节点的后继节点
public Node getSuccessor(Node delNode){
Node successor = delNode;
Node current = delNode.rightChild;
while(current != null){
successor = current;
current = current.leftChild;
}
//找到了后继节点,进一步判断
if(successor != delNode.rightChild){
//当后继节点不是要删除节点的右子节点时
successor.parent.leftChild = successor.rightChild;
if(successor.rightChild != null){
successor.rightChild.parent = successor.parent;
}
//将后继节点与要删除节点的右边连起来
delNode.rightChild.parent = successor;
successor.rightChild = delNode.rightChild;
}
return successor;
}
}
//定义节点类
class Node {
//声明为public,方便存取
public int data;//数据域
public Node parent;//父节点
public Node leftChild;//左子结点
public Node rightChild;//右子节点
public Node(int data){
this.data = data;
}
}