数据结构的底层实现今天植树节,做个二分搜索树吧
Posted Learn Java Now
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构的底层实现今天植树节,做个二分搜索树吧相关的知识,希望对你有一定的参考价值。
二分搜索树(Binary Search Tree),也称二叉查找树,有序二叉树,排序二叉树,在查找可比较的数据时有快速查找的优势,它具备如下特性:
* 1.每个节点的数值必须大于他的左子树
* 2.每个节点的数值必须小于他的右子树
* 3.节点中的数据必须具备可比较性
* 4.节点中的数据不重复
二分搜索树示例图
其主要设计思想为,通过一个内部类(节点),定义该类用于存储数据,在其中声明两个节点,分别为左子树(left)和右子树(right),在主类中声明一个节点,称为根节点 (root)。
public class BinarySerchTree<E extends Comparable> {
private class Node{
public E e;
Node left,right;
public Node(E e) {
this.e=e;
left=null;
right=null;
}
}
private Node root;
private int size;
public BinarySerchTree() {
root=null;
size=0;
}
}
其中比较值得注意的是它的增删查操作,以及树的遍历。
向树中添加元素:
使用递归的思想,向树中添加元素,先把该元素与根元素对比,大放右边,小放左边,以此类推,直到它达到合适的位置。删除和查找操作均使用类似思想。
private Node add(Node node,E e) {
//解决最终问题,如果查到的该节点已经为空,
//说明已经找到该元素要插入的位置,新建一个包含了该元素的节点返回
if(node==null) {
size++;
return new Node(e);
}
//进行递归
if(e.compareTo(node.e)<0) {
node.left=add(node.left,e);
}else if(e.compareTo(node.e)>0) {
node.right=add(node.right,e);
}
return node;
}
树的遍历:
二分搜索树的遍历分为前序遍历,中序遍历,后序遍历。是因为在遍历树的时候,每个节点其实执行了三次,在节点第一次执行时遍历数据称为前序遍历,以此类推,中序后序为二,三次执行遍历。
//二分搜索树的后序遍历,访问节点放在left和right中间
private void postOrder(Node node) {
if(node==null) return;
postOrder(node.left);
System.out.println(node.e);
postOrder(node.right);
}
熟练使用二分搜索树的标识是能够将一个遍历过的数据按照前序,中序,后续遍历的方式反向推回树的结构。
以上是今天的植树节收获
以下是完整的实现代码,仅供学习交流:
/* 本次实现不包含重复数据,重复元素可以改变二分搜索树的定义来实现
* 二分搜索树必须满足以下条件:
* 1.每个节点的数值必须大于他的左子树
* 2.每个节点的数值必须小于他的右子树
* 3.节点中的数据必须具备可比较性
*/
public class BinarySerchTree<E extends Comparable> {
private class Node{
public E e;
Node left,right;
public Node(E e) {
this.e=e;
left=null;
right=null;
}
}
private Node root;
private int size;
public BinarySerchTree() {
root=null;
size=0;
}
public int getSize() {
return size;
}
public boolean isEmpty() {
return size==0;
}
//用递归实现添加操作,用户使用
public void add(E e) {
root=add(root,e);
}
//增加操作的递归算法,程序使用
private Node add(Node node,E e) {
//解决基本问题,最终判读
/*
if(e.equals(node.e)) {
//如果已经有了,直接返回
return;
}
//如果e小于当前节点且当前节点的左子为空
else if(e.compareTo(node.e)<0&&node.left==null){
node.left=new Node(e);
size++;
return;
}else if(e.compareTo(node.e)>0&&node.right==null) {
node.right=new Node(e);
size++;
return;
}
*/
//上述条件其实还没有递归到底
if(node==null) {
size++;
return new Node(e);
}
//进行递归
if(e.compareTo(node.e)<0) {
node.left=add(node.left,e);
}else if(e.compareTo(node.e)>0) {
node.right=add(node.right,e);
}
return node;
}
//查找元素,递归,用户使用
public boolean contains(E e) {
return contains(root,e);
}
//查找元素,递归,程序使用
private boolean contains(Node node,E e) {
//基本情况
if(node==null) {
return false;
}
if(e.compareTo(node.e)==0) {
return true;
}
//进行递归
else if(e.compareTo(node.e)<0) {
return contains(node.left,e);
}
else {
return contains(node.right,e);
}
}
public String a(int x) {
StringBuilder res=new StringBuilder();
for(int i=0;i<x;i++) {
res.append("--");
}
return res.toString();
}
//二分搜索树的中序遍历,访问节点放在left和right中间
public void inOrder() {
inOrder(root);
}
private void inOrder(Node node) {
if(node==null) return;
inOrder(node.left);
System.out.println(node.e);
inOrder(node.right);
}
//二分搜索树的后序遍历,访问节点放在left和right中间
public void postOrder() {
postOrder(root);
}
private void postOrder(Node node) {
if(node==null) return;
postOrder(node.left);
System.out.println(node.e);
postOrder(node.right);
}
//二分搜索树的前序遍历
private void preOrder(Node node,int depth) {
//终止情况
if(node==null) {
//System.out.println(a(depth)+"depth:"+depth+" bopreOrderom return ");
return;
}
System.out.println(node.e);
//System.out.println(a(depth)+"depth:"+depth+" node="+node.e+" 修改");
//System.out.println(a(depth)+"depth:"+depth+" left");
preOrder(node.left,depth+1);
//System.out.println(a(depth)+"depth:"+depth+" right");
preOrder(node.right,depth+1);
}
public void preOrder() {
preOrder(root,0);
}
//二分搜索树的前序遍历非递归写法,模仿系统栈
public void traverse() {
Stack<Node> stack=new Stack<>();
stack.push(root);
while(!stack.isEmpty()) {
Node cur=stack.pop();
System.out.println(cur.e);
//先压入右节点,再压入左节点
if(cur.right!=null) {
stack.push(cur.right);
}
if(cur.left!=null) {
stack.push(cur.left);
}
}
}
//二分搜索树的层序遍历,广度优先遍历,非递归,借助队列
public void depthTraverse() {
MyQueue<Node> q=new LoopQueue<>();
q.enQueue(root);
while(q.getFront()!=null) {
Node cur=q.deQueue();
System.out.println(cur.e);
//入队
if(cur.left!=null) {
q.enQueue(cur.left);
}
if(cur.right!=null) {
q.enQueue(cur.right);
}
}
}
//删除节点
//删除最小值
public E removeMin() {
E ret=findMin().e;
root=removeMin(root);
return ret;
}
/**
* @param 删除掉以node为根的最小节点
* @return 返回新的二分搜索树的根
*/
private Node removeMin(Node node) {
//如果min是一个叶子节点,直接删除;如果min有右子树,将右子树给上面的节点
if(node.left==null) {
Node right=node.right; //储存最小节点的右子树,如果right为null也无所谓
node.right=null; //删掉
size--;
return right;
}
node.left=removeMin(node.left);
return node;
}
public Node findMin() {
if(isEmpty()) {
throw new IllegalArgumentException("empty tree");
}
return findMin(root);
}
private Node findMin(Node node) {
if(node.left==null) {
return node;
}
return findMin(node.left);
}
//删除最大值
public E removeMax() {
E ret=findMax().e;
root=removeMax(root);
return ret;
}
/**
* @param 删除掉以node为根的最大节点
* @return 返回新的二分搜索树的根
*/
private Node removeMax(Node node) {
if(node.right==null) {//node为最大节点
Node left= node.left; //储存最大节点的左节点
node.right=null; //删掉
return left;
}
node.right=removeMax(node.right);
return node;
}
public Node findMax() {
if(isEmpty()) {
throw new IllegalArgumentException("empty tree");
}
return findMin(root);
}
private Node findMax(Node node) {
if(node.right==null) {
return node;
}
return findMax(node.right);
}
//删除任意节点
public boolean remove(E e) {
if(contains(e)) {
root=remove(root,e);
return true;
}
return false;
}
/**
* 在以node为根节点的二分搜索树中删除含有元素e的节点
*
* @param node 二分搜索树的根
* @param e 要删除的元素
* @return 返还新的二分搜索树的根
*
*/
private Node remove(Node node,E e) {
if(node==null) return null;
if(e.compareTo(node.e)>0) {
node.right=remove(node.right,e);
return node;
}else if(e.compareTo(node.e)<0) {
node.left=remove(node.left,e);
return node;
} else { //node是要删除的节点
//如果node左子树为空
if(node.left==null) {
Node right=node.right;
node=null;
size--;
return right;
}
//如果node右子树为空
else if(node.right==null) {
Node left=node.left;
node=null;
size--;
return left;
}
//node左右子树都不为空
//找到比node要大的最小的节点与之交换,也就是其右子树中最左边的节点
else {
Node ret=findMin(node.right);//ret是node右子树中最小的节点
ret.right=removeMin(node.right);//ret的右子树指向删掉自己后的根
ret.left=node.left;
node=null;
return ret;
}
}
}
public String toString() {
StringBuilder res=new StringBuilder();
generateString(root,0,res);
return res.toString();
}
private void generateString(Node node,int depth,StringBuilder res) {
if(node==null) {
res.append(a(depth)+"null\n");
return;
}
res.append(a(depth)+node.e+"\n");
generateString(node.left,depth+1,res);
generateString(node.right,depth+1,res);
}
}
以上是关于数据结构的底层实现今天植树节,做个二分搜索树吧的主要内容,如果未能解决你的问题,请参考以下文章