Java数据结构(四):二叉树

版权声明:转载请注明原文地址。 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个节点。

二叉树的应用

       我们知道,在有序数组中,可以快速找到特定的值,但是想在有序数组中插入一个新的数据,就必须首先找出新数据项插入的位置,然后将比新数据大的数据向后移动一位,来给新的数据腾出空间,删除同理,这样移动非常的费时。显而易见,如果要做很多的插入和删除操作,就不该选用有序数组。
       链表中可以快速添加和删除某个数据,但是在链表中查找数据必须从头开始访问链表的每一个数据,知道找到该数据为止,这个过程很慢。
       树这种数据结构,既能像链表那样快速的插入和删除,又能像有序数组那样快速查找。二叉搜索树有如下特点:一个节点的左子节点的值小于这个节点,右子节点的值大于或等于这个节点。插入一个节点需要根据这个规则进行插入。
       删除节点时二叉搜索树中最复杂的操作,但是删除节点在很多树的应用中又非常重要,所以详细研究并总结以下特点。删除节点时,首先找到节点,这个要删除的节点可能有三种情况需要考虑:

  • 该节点是叶节点,没有子节点(简单)
  • 该节点有一个子节点(比较简单)
  • 该节点有两个子节点(复杂)
  1. 要删除的节点是叶节点,没有子节点:
           要删除叶节点,只需要改变该节点的父节点对应子节点的值即可,由指向该叶节点改为null就可以了。垃圾回收器会自动回收该叶节点,不需要手动删掉。
  2. 要删除的节点只有一个子节点:
           当节点有一个子节点时,这个节点只有两个连接:连向父节点和连向它唯一的子节点。需要从这个序列中剪断这个节点,把它的子节点直接连到它的父节点上即可,这个过程要求改变父节点适当的引用(左子节点还是右子节点),指向要删除节点的子节点即可。
  3. 要删除的节点有两个子节点:
           这种情况最复杂,如果要删除有两个子节点的节点,就不能只用它的一个子节点代替它,因为它的子节点本身还可能有子节点,简单的用子节点代替不能保证二叉树的有序性。所以要用另一种办法,利用要删除节点的中序后继来代替该节点。那么如何找后继节点呢?首先得找到要删除的节点的右子节点,它的关键字值一定比待删除节点的大。然后转到待删除节点右子节点的左子节点那里(如果有的话),然后到这个左子节点的左子节点,以此类推,顺着左子节点的路径一直向下找,这个路径上的最后一个左子节点就是待删除节点的后继。如果待删除节点的右子节点没有左子节点,那么这个右子节点本身就是后继。
           找到中序后继后,就要开始删除了,如果后继节点不是待删除节点的右子节点,要执行四个步骤:
    - 把后继父节点的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;
	}
}



猜你喜欢

转载自blog.csdn.net/qq_39240270/article/details/86847613