源码地址:https://gitee.com/zjtMeng/data_structure
一、什么是二叉树
二叉排序树又叫二叉查找树或者二叉搜索树,它首先是一个二叉树,而且必须满足下面的条件:
1)若左子树不空,则左子树上所有结点的值均小于它的根节点的值;
2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值
3)左、右子树也分别为二叉排序树
二、创建二叉树
1.首先创建树节点
树节点包含4个信息:键值、数据域和两个指针域。
public class Node {
//键值(存放索引)
public long value;
//数据域(存储数据)
public String sData;
//指针域
public Node leftNode;
//指针域
public Node rightNode;
public Node(long value, String sData) {
this.value = value;
this.sData = sData;
}
}
2.开始创建二叉树
public class Tree {
public Node root;
//待补充方法
}
1.插入方法
public void insert(long value, String string){
Node newNode = new Node(value,string);
Node current = root;
Node parent;
if (root == null){
root = newNode;
}
else {
while (true){
parent = current;
if (current.value > value){
current = current.leftNode;
if (current ==null){
parent.leftNode = newNode;
return;
}
}
else {
current = current.rightNode;
if (current==null){
parent.rightNode = newNode;
return;
}
}
}
}
}
2.查找指定索引的树节点
public Node find(long value){
Node current = root;
while (current.value != value){
if (current.value > value){
current =current.leftNode;
}
else {
current = current.rightNode;
}
if (current == null){
return null;
}
}
return current;
}
3.1 遍历——前序遍历(根节点最前遍历,即根、左、右)
前序遍历的规则:(1)访问根节点
(2)前序遍历左子树
(3)前序遍历右子树
前序遍历:只要当前树节点存在左子树,就优先遍历左子树,直到没有当前节点不存在左子树,这时候才开始遍历右子树,在二叉树为空的时候,结束返回。。如图所示二叉树:
前序遍历结果为:ABDECF
递归方法:
public void frontOrder(Node localNode){
if (localNode != null){
System.out.println(localNode.value+", "+localNode.sData);
frontOrder(localNode.leftNode);
frontOrder(localNode.rightNode);
}
}
非递归方法:通过栈来存放
public void preOrder(Node localNode){
Stack<Node> stack = new Stack<>();
while (localNode != null || !stack.empty()){
while (localNode != null){
System.out.println(localNode.sData + "");
stack.push(localNode);
localNode = localNode.leftNode;
}
//切换到右节点
if (!stack.empty()){
localNode = stack.pop();
localNode = localNode.rightNode;
}
}
}
3.2 遍历——中序遍历(根节点中间遍历,即左、根、右)
中序遍历的规则:
(1)中序遍历左子树
(2)访问根节点
(3)中序遍历右子树
中序遍历:中序遍历返回的节点是有序的,遍历可以记为左根右,也就是说在二叉树的遍历过程中,首先要遍历二叉树的左子树,接着遍历根节点,最后遍历右子树,在二叉树为空的时候,结束返回。如图所示二叉树:
中序遍历结果为:DBEAFC (可以理解D<B<E<A<F<C)
递归方法:
public void inOrder(Node localNode){
if (localNode !=null) {
//中序遍历左子树
inOrder(localNode.leftNode);
//访问根节点
System.out.println(localNode.value+", "+localNode.sData);
//中序遍历右子树
inOrder(localNode.rightNode);
}
}
非递归方法:
public void midOrder(Node localNode){
Stack<Node> stack = new Stack<>();
while (localNode != null || !stack.empty()){
while (localNode != null){
stack.push(localNode);
localNode = localNode.leftNode;
}
if (!stack.empty()){
localNode = stack.pop();
System.out.println(localNode.sData + "");
localNode = localNode.rightNode;
}
}
}
3.3 遍历——后序遍历(根节点最后遍历,即左、右、根)
后序遍历二叉树的规则:
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根节点
后序遍历:反推可以得出树的结构,后序遍历可以记为左右根,也就是说在二叉树的遍历过程中,首先按照后序遍历的规则遍历左子树,接着按照后序遍历的规则遍历右子树,最后访问根节点。在二叉树为空的时候,结束返回。如图所示二叉树:
中序遍历结果为:DEBFCA
递归遍历:
/*
* 后序遍历(反推可以得出树的结构)
*/
public void afterOrder(Node localNode) {
if (localNode != null) {
//后序遍历左子树
afterOrder(localNode.leftNode);
//后序遍历右子树
afterOrder(localNode.rightNode);
//访问根节点
System.out.println(localNode.value+", "+localNode.sData);
}
}
非递归遍历:
public void posOrder(Node localNode)
{
Stack<Node> stack1 = new Stack<>();
Stack<Integer> stack2 = new Stack<>();
int i = 1;
while(localNode != null || !stack1.empty())
{
while (localNode != null)
{
stack1.push(localNode);
stack2.push(0);
localNode = localNode.leftNode;
}
while(!stack1.empty() && stack2.peek() == i)
{
stack2.pop();
System.out.print(stack1.pop().sData + " ");
}
if(!stack1.empty())
{
stack2.pop();
stack2.push(1);
localNode = stack1.peek();
localNode = localNode.rightNode;
}
}
}
三种递归遍历的效果:
4. 获取中序后继节点
这是当删除节点的两个子节点都存在时,需要用的中序后继方法。
//获取中序后继节点
public Node getSubNode(Node delNode) {
Node subNode = delNode;
//中序后继节点的父节点
Node subParent = delNode;
Node current = delNode.rightNode;
while (current !=null) {
subParent = subNode;
subNode = current;
current = current.leftNode;
}
if (subNode != delNode.rightNode) {
subParent.leftNode = subNode.rightNode;
subNode.rightNode = delNode.rightNode;
}
return subNode;
}
5. 删除树节点
/*
* 删除节点
*/
public boolean delete(long value) {
//引用当前节点
Node current = root;
//引用当前节点的父节点
Node parent = root;
//判断是否是左子节点
boolean isLeftNode = true;
//循环,只要查找值value不等于当前节点的数据项data,就继续循环,直至找到位置或者遍历完所有节点
while (current.value != value) {
parent = current;
//进行比较,查找当前值和当前节点的大小
if (current.value >value) {
current = current.leftNode;
isLeftNode = true;
}
else {
current = current.rightNode;
isLeftNode = false;
}
//如果查不到,则返回false
if (current == null) {
return false;
}
}
//删除叶子节点,即该节点没有子节点
if (current.leftNode ==null && current.rightNode == null) {
if (current == root) {
root = null;
}
//如果它是父节点的子节点
else if (isLeftNode) {
parent.leftNode = null;
}
else {
parent.rightNode = null;
}
}
//如果该节点有一个左节点
else if (current.rightNode == null) {
if (current == root) {
root = current.leftNode;
}
else if (isLeftNode) {
parent.leftNode = current.leftNode;
}else {
parent.rightNode = current.leftNode;
}
}
//如果该节点有一个右节点
else if (current.leftNode == null) {
if (current == root ) {
root = current.leftNode;
}
else if (isLeftNode) {
parent.leftNode = current.rightNode;
}else {
parent.leftNode = current.rightNode;
}
}
//如果该节点有左节点和右节点
else{
Node subNode = getSubNode(current);
if (current == root) {
root = subNode;
}else if (isLeftNode) {
parent.leftNode = subNode;
}else {
parent.rightNode = subNode;
}
subNode.leftNode = current.leftNode;
}
return true;
}