private class Node { private K key;//键 private V value;//值 private Node left;//左子结点 private Node right;//右子结点 public Node(K key,V value,Node left,Node right){ this.key=key; this.value=value; this.left=left; this.right=right; } }
下面,我们接着来分析二叉查找树的API设计
类名: BinaryTree<K extends Comparable<K>,V value>
成员变量:
private Node root 记录根结点
private int N 记录树中元素个数
成员方法:
public void put(K key,V value) 向树中插入一个键值对
private Node put(Node,K key,V value) 向树中指定结点处插入键值对
public V get(K key) 查找树中key对应值
private V get(Node x,K key) 从树中指定节点x中查key对应值
public void delete(K key) 根据key删除键值对
private Node delete(Node x,K key) 根据key删除树中指定结点处的键值对
public int size() 获取树中元素个数
BinaryTree 完整的java代码实现
package com.tingcream.alg.tree; import com.tingcream.alg.linear.Queue; /** * 二分查找树类,特性 * 1、每个结点最多有两个子节点(左子节点、右子节点) * 2、左子节点存储的元素比当前节点小,右子节点存储的元素比当前节点大 * * BinaryTree * */ public class BinaryTree<K extends Comparable<K>,V> { private Node root;//表示树的根结点 private int N;//记录数中元素个数 //获取数量 public int size(){ return this.N; } public boolean isEmpty(){ return this.N<=0; } //向树中put一个元素key-value public void put(K key, V value){ //调用重载的put方法 root=put(root,key,value); } //向指定的结点x中添加kv,返回添加后的x结点 public Node put(Node x,K key, V value){ //如果x为空,则new一个新的结点作为根结点 if(x==null){ N++; return new Node(key,value,null,null); } //如果x不为空 // 比较x结点的键和key的大小 //如果key小于 x结点的键,则继续找x结点的左子树 //如果key大于 x结点的键,则继续找x结点的右子树 //如果key等于x 结点的键,则替换x结点的值为value int cmp=key.compareTo(x.key); if(cmp>0){ x.right= put(x.right,key,value); }else if(cmp<0){ x.left=put(x.left,key,value); }else{//key与当前结点x的键相等,则替换x结点的value值 x.value=value; } return x; } //查询树中指定key对应的value public V get(K key){ return get(root,key); } //从指定的结点x中,查找key对应的值 public V get(Node x,K key){ //如果结点x为null if(x==null){ return null ; } int cmp=key.compareTo(x.key); if(cmp>0){ // 如果key大于 x结点的键,则继续找x结点的右子树 return get(x.right,key); }else if(cmp<0){ //如果key小于 x结点的键,则继续找x结点的左子树 return get(x.left,key); }else{ //如果key等于x 结点的键,则找到了键为key的结点,返回这个结点的value即可 return x.value; } } //删除树中key对应的结点 public void delete(K key){ delete(root,key); } //删除指定结点x中的key对应的结点,并返回被删除的结点x public Node delete(Node x,K key){ //x树为null if(x==null){ return null; } //x树不为null int cmp=key.compareTo(x.key); if(cmp>0){ // 如果key大于 x结点的键,则继续找x结点的右子树 x.right=delete(x.right,key); }else if(cmp<0){ //如果key小于 x结点的键,则继续找x结点的左子树 x.left=delete(x.left,key); }else{ //如果key等于x结点的键,则找到了我们要删除的节点x //元素个数减一 N--; //如果x没有右子节点,那么直接返回左节点 if(x.right==null){ return x.left; } //x有右子节点,没有左边子节点,返回右子节点 if(x.left==null ){ return x.right; } //结点x既有左子结点又有右子结点 //找到右子树中的最小结点 if(x.right.left==null){ x.right.left=x.left; x=x.right; return x; }else{ Node minNode= x.right; while(minNode.left!=null){ minNode= minNode.left; } //删除右子树中最小的结点 Node n=x.right; while(n.left!=null){ if(n.left.left==null){ n.left=null; //删除右子树中最小的结点 ,断开left引用 }else{ n=n.left; } } //交换x和minNode节点 // //让x结点的左子树成为minNode的左子树 // //让x结点的右子树成为minNode的右子树 // minNode.left=x.left; // minNode.right=x.right; // //让x结点的父亲结点指向minNode // x=minNode; //x结点不动,用minNode结点中的key、value替换x节点中的key、value x.key=minNode.key; x.value=minNode.value; } } return x; } //查找二叉查找树中的最小的键 public K min(){ if(root==null){ return null; } return min(root).key; } //找出最小键所在结点 private Node min(Node x){ //方式二 :while循环 Node n=x; while(n.left!=null){ n=n.left; } return n; } //查找二叉查找树中的最大的键 public K max(){ if(size()>0){ return null; } return max(root).key; } //找出最大键所在结点 private Node max(Node x){ //方式二 :while循环 Node n=x; while(n.right!=null){ n=n.right; } return n; } //使用前序遍历,获取整个树中的所有的键 public Queue<K> preErgodic(){ Queue<K> keys= new Queue<K>();//new一个空队列keys preErgodic(root,keys); return keys; } //使用前序遍历,把指定树x中的所有键放入到keys队列中 private void preErgodic(Node x,Queue<K> keys){ //如果x树为空,则直接return (安全性校验) if(x==null){ return; } //把x结点的key放入到keys中 //递归遍历x结点的左子树 //递归遍历x结点的右子树 //将x树的根结点入队 keys.enqueue(x.key); //如果x树有左子树,则继续遍历它的左子树 if(x.left!=null){ preErgodic(x.left,keys); } //如果x树有右子树,则继续遍历它的右子树 if(x.right!=null){ preErgodic(x.right,keys); } } //使用中序遍历,获取整个树中的所有的键 public Queue<K> midErgodic(){ Queue<K> keys= new Queue<K>();//new一个空队列keys midErgodic(root,keys); return keys; } //使用中序遍历,把指定树x中的所有键放入到keys队列中 private void midErgodic(Node x,Queue<K> keys){ //如果x树为空,则直接return (安全性校验) if(x==null){ return; } //把x结点的key放入到keys中 //递归遍历x结点的左子树 //递归遍历x结点的右子树 //如果x树有左子树,则继续遍历它的左子树 if(x.left!=null){ midErgodic(x.left,keys); } //将x树的根结点入队 keys.enqueue(x.key); //如果x树有右子树,则继续遍历它的右子树 if(x.right!=null){ midErgodic(x.right,keys); } } //使用后序遍历,获取整个树中的所有的键 public Queue<K> afterErgodic(){ Queue<K> keys= new Queue<K>();//new一个空队列keys afterErgodic(root,keys); return keys; } //使用后序遍历,把指定树x中的所有键放入到keys队列中 private void afterErgodic(Node x,Queue<K> keys){ //如果x树为空,则直接return (安全性校验) if(x==null){ return; } //把x结点的key放入到keys中 //递归遍历x结点的左子树 //递归遍历x结点的右子树 //如果x树有左子树,则继续遍历它的左子树 if(x.left!=null){ afterErgodic(x.left,keys); } //如果x树有右子树,则继续遍历它的右子树 if(x.right!=null){ afterErgodic(x.right,keys); } //将x树的根结点入队 keys.enqueue(x.key); } //层序遍历,遍历整个树所有key加入队列 (广度优先 ) public Queue<K> layerErgodic(){ if(isEmpty()){ return null; } //定义两个队列,分别存储树中的key和树中的节点 Queue<K> keys=new Queue();//要返回的keys 队列 Queue<Node> nodes=new Queue();//节点队列 //向把root结点放入nodes队列 nodes.enqueue(root); while(!nodes.isEmpty()){ //出队一个结点,得到其key放入keys队列 Node n= nodes.dequeue(); keys.enqueue(n.key); //判断新出队的节点是否有左子节点,如果有则将其左子节点入队 if(n.left!=null){ nodes.enqueue(n.left); } //判断新出队的节点是否有右子节点,如果有则将其右子节点入队 if(n.right!=null){ nodes.enqueue(n.right); } } return keys; } //获取整个树最大深度 public int maxDepth(){ if(isEmpty()){ return 0; } return maxDepth(root); } //获取指定树的最大深度 private int maxDepth(Node x){ if(x==null){ return 0; } //获取x结点的左子树的最大深度 //获取x结点的右子树的最大深度 //得到x节点的左右子树最大深度的较大值,将较大值+1 即可 int max=0;//x的最大深度 int maxL=0;//x的左子树的最大深度 int maxR=0;//x的右子树的最大深度 if(x.left!=null){ maxL= maxDepth(x.left); } if(x.right!=null){ maxR= maxDepth(x.right); } max=(maxL>maxR)?maxL+1:maxR+1; return max; } //内部类Node 表示结点 private class Node { private K key;//键 private V value;//值 private Node left;//左子结点 private Node right;//右子结点 public Node(K key,V value,Node left,Node right){ this.key=key; this.value=value; this.left=left; this.right=right; } } }
Copyright © 叮叮声的奶酪 版权所有
备案号:鄂ICP备17018671号-1