树的学习Part2(Java实现)
包括树的各种遍历方法以及BST的实现
4.2.3 二叉树的遍历与重建 递归和非递归实现
public class Tree<T> {
class BinaryNode<T>{
T element;
BinaryNode left;
BinaryNode right;
}
@Test
public void test(){
String postFixString="ab+cde+**";
buildInfixTree(postFixString);
}
public BinaryNode<String> buildInfixTree(String postFixString){
BinaryNode<String> root=new BinaryNode<>();
Stack<BinaryNode<String>> stack=new Stack<>();
for(int i=0;i<postFixString.length();i++){
String c=String.valueOf(postFixString.charAt(i));
if(check(c)){//检测到运算符,弹出两个操作数
BinaryNode<String> node1=new BinaryNode<>();//例如压入ab,那么弹出ba,node1是b,node2是a
BinaryNode<String> node2=new BinaryNode<>();
node1=stack.pop();
node2=stack.pop();
BinaryNode<String> sNode=new BinaryNode<>();
sNode.element=c;
sNode.left=node2;
sNode.right=node1;
stack.push(sNode);
}else {
BinaryNode<String> node=new BinaryNode<>();
node.element=c;
stack.push(node);
}
}
return stack.peek();
}
/**
* 检查是不是运算符
* @param c
* @return
*/
public boolean check(String c){
if(c.equals("+")||c.equals("-")||c.equals("*")||c.equals("/")){
return true;
}
return false;
}
@Test
public void fixTest(){
String postFixString="ab+cde+**";
BinaryNode<String> root=buildInfixTree(postFixString);
preTest(root);
System.out.println(sb.toString());
sb=null;
sb=new StringBuilder();
inTest(root);
System.out.println(sb.toString());
sb=null;
sb=new StringBuilder();
postTest(root);
System.out.println(sb.toString());
sb=null;
sb=new StringBuilder();
preTestNo(root);
System.out.println(sb.toString());
sb=null;
sb=new StringBuilder();
inTestNo(root);
System.out.println(sb.toString());
sb=null;
sb=new StringBuilder();
postTestNo(root);
System.out.println(sb.toString());
}
private StringBuilder sb=new StringBuilder();
/**
* 前序递归遍历
* @param node
*/
public void preTest(BinaryNode<String> node){
if(node!=null){
sb.append(node.element);
preTest(node.left);
preTest(node.right);
}
}
/**
* 前序非递归
* @param node
*/
public void preTestNo(BinaryNode<String> node){
Stack<BinaryNode<String>> stack=new Stack<>();
while (node!=null||stack.empty()==false){
if(node!=null){
sb.append(node.element);
stack.push(node);
node=node.left;
}else {
node=stack.pop();
node=node.right;
}
}
}
/**
* 中序递归遍历
* @param node
*/
public void inTest(BinaryNode<String> node){
if(node!=null){
inTest(node.left);
sb.append(node.element);
inTest(node.right);
}
}
public void inTestNo(BinaryNode<String> node){
Stack<BinaryNode<String>> stack=new Stack<>();
while(node!=null||!stack.empty()){
if(node!=null){
stack.push(node);
node=node.left;
}else {
node=stack.pop();
sb.append(node.element);
node=node.right;
}
}
}
/**
* 后序递归遍历
* @param node
*/
public void postTest(BinaryNode<String> node){
if(node!=null){
postTest(node.left);
postTest(node.right);
sb.append(node.element);
}
}
/**
* 后序非递归遍历
* 可以总结出规律:
*
* 节点为空,pop 出一个,访问其左孩子(这里是左孩子)
* 节点不为空,入栈,记录,然后访问其右孩子
* 逆转记录
*
* 从本质上来理解,后序遍历实际上是前序遍历的右侧形式(前序遍历是左边优先)
* @param node
*/
public void postTestNo(BinaryNode<String> node){
Stack<BinaryNode<String>> stack=new Stack<>();
while(node!=null||!stack.empty()){
if(node!=null){
stack.push(node);
sb.append(node.element);
node=node.right;
}else {
node=stack.pop();
node=node.left;
}
}
sb=sb.reverse();
}
@Test
public void rebuildTreeTest(){
BinaryNode<String> currentNode=new BinaryNode<>();
// String pre="12473568";
// String in="47215386";
String pre[]={"1","2","4","7","3","5","6","8"};
String in[]={"4","7","2","1","5","3","8","6"};
rebuildPreIn(currentNode,pre,in);
System.out.println(currentNode.element);
}
/**
*
* @param fromArray 原始数组
* @param startIndex 起始索引
* @param endIndex 结束索引
*/
public String[] copyElement(String[] fromArray,int startIndex,int endIndex){
String[] toArray=new String[endIndex-startIndex+1];
int index=0;
for(int i=startIndex;i<=endIndex;i++){
toArray[index++]=fromArray[i];
}
return toArray;
}
public int getElementIndexFromArray(String[] array,String target){
for(int i=0;i<array.length;i++){
if(array[i].equals(target)){
return i;
}
}
return -1;
}
public void rebuildPreIn(BinaryNode<String> currentNode,String pre[],String in[]){
//BinaryNode<String> currentNode=new BinaryNode<>();
if(currentNode==null){
return;
}
currentNode.element=pre[0];
//String leftInTree=in.substring(0,in.indexOf(currentNode.element));
String leftInTree[]=copyElement(in,0,getElementIndexFromArray(in,currentNode.element)-1);
//String rightInTree=in.substring(in.indexOf(currentNode.element)+1);
String rightInTree[]=copyElement(in,getElementIndexFromArray(in,currentNode.element)+1,in.length-1);
//String leftPreTree=pre.substring(1,leftInTree.length()+1);
String[] leftPreTree=copyElement(pre,1,leftInTree.length);
//String rightPreTree=pre.substring(leftPreTree.length()+1);
String[] rightPreTree=copyElement(pre,leftPreTree.length+1,pre.length-1);
BinaryNode<String> leftNode=new BinaryNode<>();
if(leftInTree.length==0&&leftPreTree.length==0){
leftNode=null;
}
BinaryNode<String> rightNode=new BinaryNode<>();
if(rightInTree.length==0&&rightPreTree.length==0){
rightNode=null;
}
currentNode.left=leftNode;
currentNode.right=rightNode;
rebuildPreIn(leftNode,leftPreTree,leftInTree);
rebuildPreIn(rightNode,rightPreTree,rightInTree);
}
/**
* 后续遍历计算树的高度
* @param node
* @return
*/
public int getTreeHigh(BinaryNode<T> node){
if(node==null){
return -1;
}else {
return 1+Math.max(getTreeHigh(node.left),getTreeHigh(node.right));
}
}
}
4.3 查找树ADT-二叉查找树
使二叉树成为二叉查找树的性质是,对于树中的每个节点X,它的左子树中所有项的值小于X中的项,它的右子树中的所有项的值大于X中项的值。
二叉查找树要求所有项都能够进行排序,需要使用Comparable接口来表述这个性质,可以用comparableTo()方法进行比较。
4.3.1 二叉查找树的基本架构
public class BinarySearchTree<T extends Comparable<? super T>> {
private static class BinaryNode<T>{
T element;
BinaryNode<T> left;
BinaryNode<T> right;
BinaryNode(T element,BinaryNode<T> left,BinaryNode<T> right){
this.element=element;
this.left=left;
this.right=right;
}
}
private BinaryNode<T> root;
/**
* 构造方法,初始化为null
*/
public BinarySearchTree(){
root=null;
}
/**
* 清空二叉树
*/
public void makeEmpty(){
root=null;
}
/**
* 判断二叉树是不是空的
* @return
*/
public boolean isEmpty(){
return root==null;
}
/**
* 判断是否包含
* @param element
* @return
*/
public boolean contains(T element,BinaryNode<T> root){
}
/**
* 寻找最小值
* @return
*/
public T findMin(){
}
/**
* 寻找最大值
* @return
*/
public T findMax(){
}
/**
* 插入元素
* @param element
*/
public void insert(T element){
}
public void remove(T element){
}
/**
* 打印二叉树
*/
public void printTree(){
}
}
4.3.2 二叉查找树节点的删除
删除相对比较困难,要遵循以下思路
- 如果节点是一个树叶,可以直接删除
- 如果节点有一个儿子,那么把它的儿子节点放在节点的位置
- 如果节点有两个儿子,一般的删除策略是用其右子树的最小数据代替该节点的数据并递归的删除那个节点,因为右子树的节点不可能有左儿子,所以第二次remove要容易
/**
* 节点递归删除
* @param element
* @param node
* @return
*/
private BinaryNode<T> remove(T element,BinaryNode<T> node){
if(node==null){
return node;
}
if(element.compareTo(node.element)<0){
node.left=remove(element,node.left);
}else if(element.compareTo(node.element)>0){
node.right=remove(element,node.right);
}else if(node.left!=null&& node.right!=null){
node.element=findMin(node).element;
node.right=remove(node.element,node.right);
}else {
if(node.left!=null){
node=node.left;
}else {
node=node.right;
}
}
return node;
}
还有另一种方式,惰性删除,也就是只标记某个节点被删除,但是不是真的删除,效率很高。
4.3.3 二叉查找树的完整实现
包含了各种方式的非递归实现和递归实现
public class BinarySearchTree<T extends Comparable<? super T>> {
private static class BinaryNode<T>{
T element;
BinaryNode<T> left;
BinaryNode<T> right;
BinaryNode(T element,BinaryNode<T> left,BinaryNode<T> right){
this.element=element;
this.left=left;
this.right=right;
}
}
private BinaryNode<T> root;
/**
* 构造方法,初始化为null
*/
public BinarySearchTree(){
root=null;
}
/**
* 清空二叉树
*/
public void makeEmpty(){
root=null;
}
/**
* 判断二叉树是不是空的
* @return
*/
public boolean isEmpty(){
return root==null;
}
public boolean contains(T element){
return contains(element,root);
}
/**
* 判断是否包含,可以使用先序遍历,中序遍历,后序遍历,中序遍历等进行遍历,,根据二叉搜索树的性质可以做一些改进
* @param element
* @return
*/
private boolean contains(T element,BinaryNode<T> node){
if(element==null||element==null){
return false;
}
Stack<BinaryNode<T>> stack=new Stack<>();
while(node!=null||!stack.isEmpty()){
if(node!=null){
if(node.element.compareTo(element)==0){
return true;
}
stack.push(node);
node=node.left;
}else {
node=stack.pop();
node=node.right;
}
}
return false;
}
/**
* 前序查找非递归实现
* @param element
* @param node
* @return
*/
private boolean contains2(T element,BinaryNode<T> node){
if(element==null||element==null){
return false;
}
Stack<BinaryNode<T>> stack=new Stack<>();
boolean flag=false;
while(node!=null||stack!=null){
if(node!=null){
if(node.element.compareTo(element)==0){//相等,返回true
return true;
}else if(node.element.compareTo(element)>0){//根节点元素大于当前值,那么就在左子树
node=node.left;
flag=false;
}else {//根节点元素小于当前值,那么切换到右子树
node=node.right;
flag=true;
}
stack.push(node);
}else {
node=stack.pop();
if(flag==true){
node=node.left;
}else {
node=node.right;
}
}
}
return false;
}
/**
* 前序递归查找二叉查找树
* @param element
* @param node
* @return
*/
private boolean contains3(T element,BinaryNode<T> node){
if(element==null||node==null){
return false;
}
if(element.compareTo(node.element)==0){
return true;
}else if(element.compareTo(node.element)>0){
return contains3(element,node.right);
}else {
return contains3(element,node.left);
}
}
/**
* 寻找最小值 非递归实现,一直查找节点的左儿子,直到某个节点的左儿子为null了,那么这个节点就是最小节点
* @return
*/
public T findMin(){
if(isEmpty()){
throw new BufferUnderflowException();
}
BinaryNode<T> node=root;
while(node.left!=null){
node=node.left;
}
return node.element;
}
/**
* 寻找最小值,递归实现,一直查找节点的左儿子,直到某个节点的左儿子为null了,那么这个节点就是最小节点
* @return
*/
private BinaryNode<T> findMin(BinaryNode<T> node){
if(node==null){
return node;
}
if(node.left==null){
return node;
}
return findMin(node.left);
}
/**
* 寻找最大值 非递归
* @return
*/
public T findMax(){
BinaryNode<T> node=root;
if(isEmpty()){
throw new BufferUnderflowException();
}
while(node.right!=null){
node=node.right;
}
return node.element;
}
/**
* 插入元素:步骤,比较元素值和当前根节点的大小,如果当前节点大于根节点,则选择右子树,当前节点小于根节点,则选择左子树,否则报错,说明节点中已有。
* 假如某个节点的左右节点都是null,并且待插入节点大于当前节点,那么插在当前节点的右儿子位置,否则成为当前节点的左儿子
* 假如某个节点的左儿子存在,右儿子为null,并且当前节点的值小于插入值,那么插在右儿子位置
* @param element
*/
public void insert(T element){
root=insert1(element,root);
}
/**
* 递归插入
* @param element
* @param node
* @return
*/
private BinaryNode<T> insert(T element,BinaryNode<T> node){
if(node==null){
return new BinaryNode<>(element,null,null);
}
if(element.compareTo(node.element)<0){
node.left=insert(element,node.left);
}else if(element.compareTo(node.element)>0){
node.right=insert(element,node.right);
}else {
}
return node;
}
/**
* 非递归插入
* @param element
* @param node
* @return
*/
private BinaryNode<T> insert1(T element,BinaryNode<T> node){
if(node==null){
return new BinaryNode<>(element,null,null);
}
BinaryNode<T> currentNode=node;
BinaryNode<T> parentNode=node;
while (currentNode!=null){
parentNode=currentNode;
if(currentNode.element.compareTo(element)>0){
currentNode=currentNode.left;
}else if(currentNode.element.compareTo(element)<0){
currentNode=currentNode.right;
}
}
if(parentNode.element.compareTo(element)>0){
parentNode.left=new BinaryNode<T>(element,null,null);
}else {
parentNode.right=new BinaryNode<>(element,null,null);
}
return node;
}
public void remove(T element){
root=remove(element,root);
}
/**
* 节点递归删除
* @param element
* @param node
* @return
*/
private BinaryNode<T> remove(T element,BinaryNode<T> node){
if(node==null){
return node;
}
if(element.compareTo(node.element)<0){
node.left=remove(element,node.left);
}else if(element.compareTo(node.element)>0){
node.right=remove(element,node.right);
}else if(node.left!=null&& node.right!=null){
node.element=findMin(node).element;
node.right=remove(node.element,node.right);
}else {
if(node.left!=null){
node=node.left;
}else {
node=node.right;
}
}
return node;
}
/**
* 中序遍历二叉搜索树
*/
public void printTree(){
BinaryNode<T> node=root;
Stack<BinaryNode<T>> stack=new Stack<>();
while(node!=null||stack.isEmpty()==false){
if(node!=null){
stack.push(node);
node=node.left;
}else {
node=stack.pop();
System.out.println(node.element);
node=node.right;
}
}
}
}