**首先来了解一下树的一些术语:
(图片来源:中国大学mooc —— 浙大数据结构)
二叉树定义:
1、每个结点最多有两颗子树,结点的度最大为2。
2、左子树和右子树是有顺序的,次序不能颠倒。
3、即使某结点只有一个子树,也要区分左右子树。
二叉树的几种状态:
特殊二叉树:
下面放一张错误的完全二叉树
一些公式做笔试题用的:
二叉树介绍完了,下面介绍一下二叉搜索树,此案例代码展示的也是搜索树二叉搜索树其实就是在二叉树的基础上加个条件:
左节点的数比根节点的数小,右边的数要比更节点数大
案例中提供的方法以及实现原理:
find:(就是在遍历过程中加个判断)
反复与当前根节点比较,比起小向左找,比起大向右找
找到就返回,找不到(==null)返回false
findMax 最大的节点是树的最右叶子结点
findMin 最小的节点是树的最左叶子结点
insert:
如果是根节点直接插
不是就先判断,比跟小往左,比跟大往右,然后不断循环,直到叶子结点,在比较,选择插到叶点的左还是右
delete
* 二叉树的删除要考虑4种情况:还要判断要删除的变量是parent的左还右
*
* 1.被删除的节点是叶子结点 :直接赋为null
* 2.被删除的节点只有左子节点:左子节点上来
* 3.被删除的节点只有右子节点:右子节点上来
* 4.被删除的结点有两个节点:取其右子节点的左叶子节点(最后一个)上来(也就是右子树中的最小元素)
* (如果其右子节点是叶子结点或者没有左子节点,直接将右子节点提上来)
display 遍历
先序遍历:(图中的数字代表打印顺序)
先序遍历是先访问根结点,再左子树,再右子树
递归思想:遍历到当前元素先打印,然后走向左右子节点
*if ( current != null)
{
print
left
right
}*
数字是打印顺序
以上图为例:一路打印到左叶子结点(A -> B ->D),下面没有啦,D转向右,右也没有,函数返回,到上一个节点B,发现又右节点F(b.right ->F),打印,再left到E,打印,E没有右,函数返回到F,F没有右,再返回到B,B的right执行完毕,返回到A,A的left执行完,执行right,打印,left。。。
非递归:和中序一样,就是打印顺序不同
中序遍历和
有了先序的经验,这两个就很好理解了,中序是先访问左子树, 再根结点,再右子树,
递归思想:先left一直到叶子结点,然后print,print完,然后right,没有右就返回上一节点,右就重复上面操作,
if (current != null )
{
left
print
right
}
非递归
后序遍历
后序是先访问左子树, 再右子树,再根结点。
其实前中后的递归遍历思想基本一样,就是print的时机不同而已
if ( current != null )
{
left
right
print
}
非递归:不会=_=
层次遍历:即每一层从左向右输出
元素需要储存有先进先出的特性,所以选用队列存储。
思想:先节点入队,然后出队打印,将左右节点入队
下面是代码:
/*
* 二叉搜索树
*
* 左节点的数比根节点的数小,右边的数要比更节点数大
*/
public class binaryTree
{
private Node root ; //根节点
private Node current ;//记录当前遍历到哪个节点了
private Node parent ;//记录遍历节点的上一个节点
public binaryTree ()
{
root = null ;
current = null ;
}
/*
* 按照规则:左边的数比根节点小,右边的数比根节点大
* 先进行反复比较,一直到最后和叶子节点比较,选择插在左还是右
*/
public void insert ( int data )
{
Node newNode = new Node ( data ) ;
//如果根节点为空,直接将新节点付给根节点
if ( root == null )
{
root = newNode ;
return ;
}
current = root ;
while ( current != null )
{
parent = current ;
//左子节点通常比根节点小,右节点大
//新数据比当前节点数据小,往左
if ( current.data > data )
{
current = current.left ;
//为空代表到底了
if ( current == null )
{
parent.left = newNode ;
}
}
//比他大,往右
else
{
current = current.right ;
if ( current == null )
{
parent.right = newNode ;
}
}
}
}
public Node find ( int data )
{
current = root ;
if ( root != null )
while ( current != null )
{
//要查找的数据比当前节点数据小,往左
if ( current.data > data )
{
current = current.left ;
}
//往右
else if ( current.data < data )
{
current = current.right ;
}
//既不大又不小,那就是=喽,返回此节点
else
{
return current ;
}
}
else
{
System.out.println ( "树为空" );
return null ;
}
//执行到这,说明current已为null , 没找到
return null ;
}
//找最大,通常情况下最大值在树的最右分支上
public Node findMax ()
{
current = root ;
parent = root ;
while ( current != null )
{
parent = current ;
current = current.right ;
}
return parent ;
}
public Node findMin ()
{
current = root ;
parent = root ;
while ( current != null )
{
parent = current ;
current = current.left ;
}
return parent ;
}
/*
*
* 二叉树的删除要考虑4种情况:
*
* 1.被删除的节点是叶子结点 :直接赋为null
* 2.被删除的节点只有左子节点:左子节点上来
* 3.被删除的节点只有右子节点:右子节点上来
* 4.被删除的结点有两个节点:取其右子节点的左叶子节点(最后一个)上来
* (如果其右子节点是叶子结点或者没有左子节点,直接将右子节点提上来)
*/
public boolean delete ( int data )
{
current = root ;
parent = root ;
boolean isLeft = true ;//用来判断当前的节点是否是左节点(根据此变量来更改parent对下一个结点引用)
while ( current.data != data )
{
parent = current ;
//为空代表没找到
if ( current == null )
return false ;
//往左
if ( current.data > data )
{
current = current.left ;
isLeft = true ;
}
//往右
else
{
current = current.right ;
isLeft = false ;
}
}
//跳出循环代表找到要删除的数据
//1.判断是否是第一种情况
if ( current.left == null && current.right == null )
{
//判断是不是根节点
if ( current == root )
{
root = null ;
return true ;
}
else
{
//isLeft是相对于parent而言,判断current是parent的左右节点,依次来改变parent中的引用
if ( isLeft == true )
{
parent.left = null ;
}
else
{
parent.right = null ;
}
current = null ;
return true ;
}
}
//2.判断是否是第二种
if ( current.left != null && current.right == null )
{
if ( current == root )
{
root = current.left ;
return true ;
}
else
{
if ( isLeft == true )
{
parent.left = current.left ;
}
else
{
parent.right = current.left ;
}
return true ;
}
}
//3.判断是否为第三种
if ( current.left == null && current.right != null )
{
if ( current == root )
{
root = current.right ;
return true ;
}
else
{
if ( isLeft == true )
{
parent.left = current.left ;
}
else
{
parent.right = current.left ;
}
return true ;
}
}
//4 。第四种 (最麻烦的一种)
if ( current.left != null && current.right != null )
{
//parent 是current的父节点
//current:要删除的节点
//sonp:用来替换节点的父节点
//son用来遍历,遍历到最后变成用来替换的节点
Node son = current.right ;
Node sonp = current.right;
boolean isNull = false ;//用于判断要删除的节点的右子节点是否左子节点
if ( son.left == null )
{
isNull = true ;
}
//如果条件成立,代表此时要删除的节点的右子节点没有左子节点
if ( isNull == true )
{
//判断current是父节点的左还是右节点
if ( isLeft == true )
{
parent.left = son ;
return true ;
}
else
{
parent.right = son ;
return true ;
}
}
//执行到这说明有左子节点,找到右节点的叶左叶子结点(也叫中序后继节点)
while ( son.left != null )
{
sonp = son ;
son = son.left ;
}
//循环结束,代表找到了此时son就是最后的节点
sonp.left = null ;//抹除son父节点对其的引用
son.right = current.right ;//将要删除的节点的右引用转移到son身上来
son.left = current.left ;
if ( current == root )
{
root = son ;
return true ;
}
else
{
if ( isLeft == true )
{
parent.left = son ;
}
else
{
parent.right = son ;
}
return true ;
}
}
//如果真能执行到这说明上面代码都不符合只能false了
return false ;
}
public void display ()
{
//
// System.out.println ( "先序遍历" );
// frontdisplay (root) ;
// System.out.println ( );
// front (root) ;
// System.out.println ( "中序遍历" );
// middisplay (root) ;
// mid(root) ;
// System.out.println ( "后序遍历" );
// lastdisplay (root) ;
// System.out.println ( );
// last ( root ) ;
levelDisplay () ;
}
//使用递归遍历
public void frontdisplay ( Node next )
{
//先序遍历(根,左,右)
if ( next != null )
{
System.out.print ( next.data + " ");
frontdisplay ( next.left ) ;
frontdisplay ( next.right ) ;
}
}
//中序:左根右
public void middisplay ( Node next )
{
if ( next != null )
{
middisplay ( next.left ) ;
System.out.print ( next.data + " " );
middisplay ( next.right ) ;
}
}
//后序:左右根
public void lastdisplay ( Node next )
{
if ( next != null )
{
lastdisplay ( next.left ) ;
lastdisplay ( next.right ) ;
System.out.print ( next.data + " " );
}
}
public void levelDisplay ()
{
Node current = root ;
queue q = new queue () ;
q.add ( current );
while ( !q.isEmpty () )
{
current = q.delete () ;
System.out.print ( current.data + " " );
if ( current.left != null )
{
q.add ( current.left );
}
if ( current.right != null )
{
q.add ( current.right );
}
}
System.out.println ( );
}
// ------------------------------------
//非递归遍历
//中序 左 根 右
public void mid ( Node n )
{
Stack s = new Stack () ;
while ( n != null || s.isEmpty () != true )
{
while ( n != null )
{
s.push ( n );
n = n.left ;
}
//跳出循环代表遍历左子节点完毕,左子节点都入栈了
if ( s.isEmpty () != true )
{
n = s.pop () ;
System.out.print ( n.data + " ");
n = n.right ;
}
}
}
//先序
public void front ( Node n )
{
Stack s = new Stack () ;
while ( n != null || s.isEmpty () != true )
{
while ( n != null )
{
System.out.print ( n.data + " ");
s.push ( n );
n = n.left ;
}
if ( s.isEmpty () != true )
{
n = s.pop () ;
n = n.right ;
}
}
}
//后序(没写出来)
public void last ( Node n )
{
}
}
stack
public class Stack
{
/*
* 栈:先进后出
*/
public Node top ;
public void push ( Node n )
{
if ( top == null )
{
top = n ;
return ;
}
n.next = top ;
top = n ;
}
public Node pop ()
{
Node temp = top ;
if ( top == null )
{
System.out.println ( "stack is empty" );
return null ;
}
top = top.next ;
return temp ;
}
public boolean isEmpty ()
{
return top == null ;
}
}
queue
**
public class queue
{
public Node front ;
public Node rear ;
public queue ()
{
front = null ;
rear = null ;
}
public void add ( Node n )
{
if ( isEmpty () )
{
front = n ;
rear = n ;
return ;
}
rear.next = n ;
rear = n;
}
public Node delete ()
{
if ( isEmpty () )
{
return null ;
}
Node temp = front ;
front = front.next ;
return temp ;
}
public boolean isEmpty ()
{
return front == null ;
}
}