数据结构的底层实现今天植树节,做个二分搜索树吧

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;                }   }     } @Override 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); } }


以上是关于数据结构的底层实现今天植树节,做个二分搜索树吧的主要内容,如果未能解决你的问题,请参考以下文章

20201011做个搜索引擎——网页蜘蛛代码编写

20201010做个搜索引擎——网页蜘蛛代码编写

数据结构-PHP 实现二分搜索树

20201006做个搜索引擎——ElasticSearch数据库设计

20201009做个搜索引擎——网页蜘蛛之工作原理

解密树的平衡:二分搜索树 → AVL自平衡树 → 红黑树