- 实现一个二叉查找树,并且支持插入、删除、查找操作
二叉查找树(Binary Search Tree),或者是一颗空树,或者是具有下列性质的二叉树:
1、若它的左子树不空,则其左子树上的所有结点的值均小于它根结点的值;
2、若它的右子树不空,则其右子树上的所有结点的值均大于它根结点的值;
3、它的左、右子树也分别为二叉查找树。
查找操作
在二叉查找树中查找x的过程如下:
1、若二叉树是空树,则查找失败。
2、若x等于根结点的数据,则查找成功,否则。
3、若x小于根结点的数据,则递归查找其左子树,否则。
4、递归查找其右子树。
插入操作
二叉树查找树b插入操作x的过程如下:
1、若b是空树,则直接将插入的结点作为根结点插入。
2、x等于b的根结点的数据的值,则直接返回,否则。
3、若x小于b的根结点的数据的值,则将x要插入的结点的位置改变为b的左子树,否则。
4、将x要出入的结点的位置改变为b的右子树。
删除操作
对于二叉查找树的删除操作(这里根据值删除,而非结点)分三种情况:
不过在此之前,我们应该确保根据给定的值找到了要删除的结点,如若没找到该结点
不会执行删除操作!
下面三种情况假设已经找到了要删除的结点。
1、如果结点为叶子结点(没有左、右子树),此时删除该结点不会玻化树的结构直接删除即可,并修改其父结点指向它的引用为null.
2、如果其结点只包含左子树,或者右子树的话,此时直接删除该结点,并将其左子树 或者右子树设置为其父结点的左子树或者右子树即可,此操作不会破坏树结构。
3、 当结点的左右子树都不空的时候,一般的删除策略是用其右子树的最小数据(容易找到)代替要删除的结点数据并递归删除该结点(此时为null),因为右子树的最小结点不可能有左孩子,所以第二次删除较为容易。
z的左子树和右子树均不空。找到z的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替z的值
static class BinaryNode {
T data;
BinaryNode left;
BinaryNode right;
public BinaryNode(T data) {
this(data,null,null);
}
public BinaryNode( T data, BinaryNode left, BinaryNode right) {
this.data =data;
this.left = left;
this.right =right;
}
public BinaryNode()
{
data =null;
this.left = left;
this.right =right;
}
}
public boolean contains(T t) {
return contains(t, rootTree);
}
public boolean contains(T t, BinaryNode node) {
if(node==null)
return false;//结点为空,查找失败
int result = t.compareTo(node.data);
if(result>0)
return contains(t,node.right);//递归查询右子树
else if(result<</SPAN>0)
return contains(t, node.left);//递归查询左子树
else
return true;
}
public T findMin() {
if(isEmpty()) {
System.out.println("二叉树为空");
return null;
}
else
return findMin(rootTree).data;
}
public T findMax() {
if(isEmpty()) {
System.out.println("二叉树为空");
return null;
}
else
return findMax(rootTree).data;
}
public BinaryNode findMin(BinaryNode node) {
if(node==null)
return null;
else if(node.left==null)
return node;
return findMin(node.left);//递归查找
}
public BinaryNode findMax(BinaryNode node) {
if(node!=null) {
while(node.right!=null)
node=node.right;
}
return node;
}
public void insert(T t) {
rootTree = insert(t, rootTree);
}
public BinaryNode insert(T t,BinaryNode node) {
if(node==null) {
//新构造一个二叉查找树
return new BinaryNode(t, null, null);
}
int result = t.compareTo(node.data);
if(result<</SPAN>0)
node.left= insert(t,node.left);
else if(result>0)
node.right= insert(t,node.right);
else
;//doNothing
return node;
}
public void remove(T t) {
rootTree = remove(t,rootTree);
}
public BinaryNode remove(T t,BinaryNode node) {
if(node == null)
return node;//没有找到,doNothing
int result = t.compareTo(node.data);
if(result>0)
node.right = remove(t,node.right);
else if(result<</SPAN>0)
node.left = remove(t,node.left);
else if(node.left!=null&&node.right!=null) {
node.data = findMin(node.right).data;
node.right = remove(node.data,node.right);
}
else
node = (node.left!=null)?node.left:node.right;
return node;
}
-
实现查找二叉查找树中某个节点的后继、前驱节点
-
实现二叉树前、中、后序以及按层遍历
递归版本
#递归版本
public class PreInPosTraversal {
public class Node{
int val;
Node left;
Node right;
public Node(int data) {
this.val = data;
}
}
#前序遍历
public static void PreTraversal(Node head) {
if(head == null) {
return;
}
System.out.print(head.val + " ");
PreTraversal(head.left);
PreTraversal(head.right);
}
#中序遍历
public static void InTraversal(Node head) {
if(head == null) {
return;
}
InTraversal(head.left);
System.out.print(head.val + " ");
InTraversal(head.right);
}
#后序遍历
public static void PosTraversal(Node head) {
if(head == null) {
return;
}
PosTraversal(head.left);
PosTraversal(head.right);
System.out.print(head.val + " ");
}
非递归版本
使用栈结构的原因:当前节点走向左孩子,走向右孩子,只有从上到下,从左到右,而没有逆序的方式。而栈具有天然的“先进后出”的特性,因此选择它。
1)先序遍历:
先序遍历是:根–左--右
如果一开始头结点不为空:首先新建一个栈,把根节点入栈;当栈不为空时,每次都使栈顶元素弹出,打印该元素的值。并访问该元素,如果该元素的右子树存在,就先将右元素入栈;如果该元素的左子树存在,就将该元素入栈。这样弹出的时候,就是先弹左再弹右。
2)中序遍历:
中序遍历是:左–根--右
如果一开始头结点不为空: 新建一个栈,如果栈不为空,且当前节点不为空,就继续循环:
(1)当前节点不为空,则当前节点入栈;当前节点往它的左子节点移动;
(2)当前节点为空,则弹出栈顶元素作为当前的头结点,打印该结点的值,并将当前节点往其右子节点移动。
3)后序遍历:
后序遍历是:左–右--根
前序(根–左--右)遍历入栈的顺序是先右再左。此时我们想到的是,可以反过来,先左再右。这样就可以做到中–左--右。这样把这个存到栈里,再一一弹出,就可以得到我们后序遍历所需的“左–右--根”。每次先不打印,而是先把要打印的数,存到一个栈里,然后依次弹出并打印栈中元素。
如果一开始头结点不为空:准备两个栈:栈1,头结点先存入栈1。只要栈1不为空时,弹出栈中元素,并将该元素存入栈2。左不为空先压左,右不为空再压右,直到结束循环。当栈2不为空时,逐个弹出栈内元素。
#非递归版本
import java.util.Stack;
public class PreInPosTraversal2 {
public class Node{
int val;
Node left;
Node right;
public Node(int data) {
this.val = data;
}
}
private static void PreTraversal(Node head) {
System.out.println("PosTraversal:");
if (head != null) {
Stack<Node> stack = new Stack<Node>();
stack.push(head);
while (!stack.isEmpty()) {
head = stack.pop();
System.out.print(head.val);
if (head.right != null) {
stack.push(head.right);
}
if (head.left != null) {
stack.push(head.left);
}
}
}
System.out.println();
}
private static void InTraversal(Node head) {
System.out.println("InTraversal:");
if(head != null) {
Stack<Node> stack = new Stack<Node>();
while(!stack.isEmpty() || head != null) {
if(head != null) {
stack.push(head);
head = head.left;
}else {
head= stack.pop();
System.out.print(head.val);
head = head.right;
}
}
System.out.println();
}
}
private static void PosTraversal(Node head) {
System.out.println("PosTraversal:");
if(head != null) {
Stack<Node> stack1 = new Stack<>();
Stack<Node> stack2 = new Stack<>();
stack1.push(head);
while(!stack1.isEmpty()) {
head = stack1.pop();
stack2.push(head);
if(head.left != null) {
stack1.push(head.left);
}
if(head.right != null) {
stack1.push(head.right);
}
}
while(!stack2.isEmpty()) {
System.out.print(stack2.pop().val);
}
}
System.out.println();
}
}
层次遍历
用BFS思想
import java.util.LinkedList;
import java.util.Queue;
public class levelTraversal {
public static class Node{
int val;
Node left;
Node right;
public Node(int data) {
this.val = data;
}
}
public static void levelTraversal(Node node) {
Queue<Node> queue = new LinkedList<Node>();
if(node != null) {
queue.add(node);
while(!queue.isEmpty()) {
Node now = queue.poll();
System.out.print(now.val + " ");
if(now.left != null) {
queue.add(now.left);
}
if(now.right != null) {
queue.add(now.right);
}
}
}
}
}
- 完成leetcode上的验证二叉搜索树(98)及二叉树 层次遍历(102,107)