この記事では、ビデオチュートリアル「高度なデータ構造へのエントリから楽しい」のliuyuboboboから来ています
...............、二分探索木、二分探索木:図は、次のような多くの翻訳があるため、漢字の数が多いため、バイナリ検索ツリーです
バイナリ検索ツリーのバイナリツリーベースが、二つ以上の特徴:
図1に示すように、ノードの比較可能性を有する二分探索木の値。
図2に示すように、その左サブツリー任意の大きさの要素、および任意の要素よりも、各ノード少ない右サブツリーより(この記事は木に重複する要素については説明していません)。
増加したバイナリ検索ツリー、取得、削除操作を実現するために、次のJavaコードを使用してください。
ノードNodeクラスの定義
private class Node {
// 节点值
public E e;
// 每个节点都可能有左节点、右节点。最后一层的节点没有左右节点可以视为left、right都为null
public Node left, right;
public Node(E e) {
this.e = e;
left = null;
right = null;
}
}
ツリーの実装
//二分搜索树
public static class BinarySearchTree<E extends Comparable<E>> {
private class Node {
// 节点值,二分搜索树的值是有比较性的,所以泛型E必须是Comparable的子类
public E e;
// 每个节点都可能有左节点、右节点。最后一层的节点没有左右节点可以视为left、right都为null
public Node left, right;
public Node(E e) {
this.e = e;
left = null;
right = null;
}
}
// 树使用root节点作为索引,增查删都从root开始操作
private Node root;
// 树中节点的数量,即树的大小
private int size;
public BinarySearchTree() {
root = null;
size = 0;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
// 在此添加增查删方法
}
最初の再帰の下では、再帰は同じタイプ、小型のアルゴリズムデータの小さな問題に実際に大きな問題、ビッグデータの分割です。
木は自然の再帰的。図ファンシーブルーボックスツリーは、木が自然な再帰的な性質を持っているので、こと、木全体の小型版です。
コードツリーノードを追加します。
// 向二分搜索树中添加新的元素e
public void add(E e){
// 第一次添加的是根节点
if(root == null){
root = new Node(e);
size ++;
}else{
add(root, e); //递归添加元素
}
}
// 向以node为根的二分搜索树中插入元素e,递归算法
private void add(Node node, E e){
// 不考虑相同元素的情况
if(e.equals(node.e)){
return;
}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(e.compareTo(node.e) < 0)
add(node.left, e);
else{
//e.compareTo(node.e) > 0 的情况
// 新增元素比当前节点大,且当前节点右边有节点,递归处理当前元素右边的节点
add(node.right, e);
}
}
次のように容易に理解上記のコード新しいノードが、大きな符号量は、合理化することができます。
public void add(E e){
// 使用root为索引
root = add(root, e);
}
public Node add(Node node, E e){
if (node == null){
// 第一次添加元素,root就为new Node(e)
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 void preOrder(){
preOrder(root);
}
public void preOrder(Node node){
if (node == null){
return;
}
// 打印当前元素值
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
順トラバーサルでは、後順トラバーサル
// 中序遍历结果从大到小排序
public void inOrder(){
inOrder(root);
}
public void inOrder(Node node){
if (node == null){
return;
}
inOrder(node.left);
System.out.println(node.e);
inOrder(node.right);
}
// 后续遍历,先遍历父节点的子节点,在遍历父节点
public void postOrder(){
postOrder(root);
}
public void postOrder(Node node){
if (node == null){
return;
}
postOrder(node.left);
postOrder(node.right);
System.out.println(node.e);
}
次の主な機能テストを書きます
public static void main(String[] args) {
BinarySearchTree<Integer> bts = new BinarySearchTree<>();
int[] nums = {28, 16, 30, 22, 13, 42, 29};
for (int num:nums){
bts.add(num);
}
System.out.println("-------前序遍历------");
bts.preOrder();
System.out.println("------中序遍历-------");
bts.inOrder();
System.out.println("------后序遍历-------");
bts.postOrde
}
以下を達成するために、後続の、深さ優先順に属する最初のトラバーサルのプリアンブル
反復再び28、16、30の最初のトラバーサルは、最終的には13,22,29,42を通過します。これは、キューの1以上幅優先トラバーサルを介して達成することができます。
上に示したフロー
チームへの1,28
左と右のノードが空でない場合は2,28チーム、そして、チームに28のノード(16、30)について置きます
-3,16-チーム、左右のノードが空でない場合は、16左右ノード(13、チームに22)
4,30チーム、左右のノードが空でない場合は、30左右ノード(29、チームに42)
コードは次のように実装されています。
// 广度优先遍历
public void levelOrder(){
if (root == null){
return;
}
Queue<Node> q = new LinkedBlockingQueue<>();
q.add(root);
while (!q.isEmpty()){
Node node = q.remove();
System.out.println(node.e);
if (node.left != null)
q.add(node.left);
if (node.right != null)
q.add(node.right);
}
}
最小の木、最大値を検索します。場合ツリーの左端ノードnode.left == NULL、最小ノードの値。node.right == NULL右端ツリーノード、ノードの最大値。
図の上記の実施形態の最小値を見つけます。同様の方法の最大値を検索し、それはほとんどの右の行を見つけることです
// 最小值元素
public E minimum(){
if(size == 0)
throw new IllegalArgumentException("空树");
Node minNode = minimum(root);
return minNode.e;
}
// 最小值节点
private Node minimum(Node node){
if( node.left == null )
return node;
return minimum(node.left);
}
// 最大值元素
public E maximum(){
if(size == 0)
throw new IllegalArgumentException("空树");
return maximum(root).e;
}
// 最大值节点
private Node maximum(Node node){
if( node.right == null )
return node;
return maximum(node.right);
}
削除最小
public E removeMin(){
// 查找最小值
E ret = minimum();
// 删除最小节点
root = removeMin(root);
// 返回最小值
return ret;
}
public Node removeMin(Node node){
// node.left == null,node必然是最小节点
if (node.left == null){
// 先保存最小节点的right
Node nodeRight = node.right;
// 再把最小节点的right指向null,以便java虚拟机回收最小节点内存
node.right = null;
size--;
return nodeRight;
}
// node.left != null,递归node.left
node.left = removeMin(node.left);
return node;
}
コードだけではなく、図面を与え、最大値を削除します。ロジックの最小値を削除するには、参照してください。
public E removeMax(){
E ret = maximum();
root = removeMax(root);
return ret;
}
private Node removeMax(Node node){
// node.right == null,节点必然是最大节点
if(node.right == null){
// 保存最大节点的right
Node leftNode = node.left;
node.left = null;
size --;
return leftNode;
}
// node.right != null,递归node.right
node.right = removeMax(node.right);
return node;
}
次のように任意の値を削除し、状況が、より多くのです。
図1に示すように、左の子ノードがnullである、最小値が削除されます。
2は、右の子ノードがnullの場合、最大値を削除することです。
図3は、子ノードが値を持っているについて、Hibbard削除アルゴリズムを使用して値を削除します。
、削除されたノードをdであると仮定すると、両方の左子供をD、子供たちが権利を持っている、あなたはD右のサブツリーを使用することができます。Hibbard削除は、コンピュータ科学者、1962年にHibbardと呼ばれ、このアルゴリズムは、単にこれですあるいは最小値D。
public void remove(E e){
remove(root, e);
}
private Node remove(Node node, E e){
if (node == null)
return null;
if (e.compareTo(node.e) < 0){
// 递归左侧节点
node.left = remove(node.left, e);
return node;
}else if (e.compareTo(node.e) > 0){
// 递归右侧节点
node.right = remove(node.right, e);
return node;
}else {
// 删除最小值
if (node.left == null){
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
// 删除最大值
if (node.right == null){
Node leftNode = node.left;
node.left=null;
size--;
return leftNode;
}
//当前node有左右孩子,找出右树中的最小节点successor
Node successor = minimum(node.right);
// successor.right = 删除当前node最小值后的树
successor.right = removeMin(node.right);
// successor.left = 当前node节点的left
successor.left = node.left;
node.left = node.right = null;
// 返回successor
return successor;
}
}
すべてのコード
//二分搜索树
public class BinarySearchTree<E extends Comparable<E>> {
private class Node {
// 节点值
public E e;
// 每个节点都可能有左节点、右节点。最后一层的节点没有左右节点可以视为left、right都为null
public Node left, right;
public Node(E e) {
this.e = e;
left = null;
right = null;
}
}
// 树使用root节点作为索引,增查删都从root开始操作
private Node root;
// 树中节点的数量,即树的大小
private int size;
public BinarySearchTree() {
root = null;
size = 0;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void add(E e){
// 使用root为索引
root = add(root, e);
}
public Node add(Node node, E e){
if (node == null){
// 第一次添加元素,root就为new Node(e)
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 void preOrder(){
preOrder(root);
}
public void preOrder(Node node){
if (node == null){
return;
}
// 打印当前元素值
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
// 中序遍历,结果从小到大排序
public void inOrder(){
inOrder(root);
}
public void inOrder(Node node){
if (node == null){
return;
}
inOrder(node.left);
System.out.println(node.e);
inOrder(node.right);
}
// 后序遍历,先遍历父节点的子节点,在遍历父节点
public void postOrder(){
postOrder(root);
}
public void postOrder(Node node){
if (node == null){
return;
}
postOrder(node.left);
postOrder(node.right);
System.out.println(node.e);
}
// 广度优先遍历
public void levelOrder(){
if (root == null){
return;
}
Queue<Node> q = new LinkedBlockingQueue<>();
q.add(root);
while (!q.isEmpty()){
Node node = q.remove();
System.out.println(node.e);
if (node.left != null)
q.add(node.left);
if (node.right != null)
q.add(node.right);
}
}
// 最小值元素
public E minimum(){
if(size == 0)
throw new IllegalArgumentException("空树");
Node minNode = minimum(root);
return minNode.e;
}
// 最小值节点
private Node minimum(Node node){
if( node.left == null )
return node;
return minimum(node.left);
}
// 最大值元素
public E maximum(){
if(size == 0)
throw new IllegalArgumentException("空树");
return maximum(root).e;
}
// 最大值节点
private Node maximum(Node node){
if( node.right == null )
return node;
return maximum(node.right);
}
public E removeMin(){
// 查找最小值
E ret = minimum();
// 删除最小节点
root = removeMin(root);
// 返回最小值
return ret;
}
public Node removeMin(Node node){
// node.left == null,node必然是最小节点
if (node.left == null){
// 先保存最小节点的right
Node nodeRight = node.right;
// 再把最小节点的right指向null,以便java虚拟机回收最小节点内存
node.right = null;
size--;
return nodeRight;
}
// node.left != null,递归node.left
node.left = removeMin(node.left);
return node;
}
public E removeMax(){
E ret = maximum();
root = removeMax(root);
return ret;
}
private Node removeMax(Node node){
// node.right == null,节点必然是最大节点
if(node.right == null){
// 保存最大节点的right
Node leftNode = node.left;
node.left = null;
size --;
return leftNode;
}
// node.right != null,递归node.right
node.right = removeMax(node.right);
return node;
}
public void remove(E e){
remove(root, e);
}
private Node remove(Node node, E e){
if (node == null)
return null;
if (e.compareTo(node.e) < 0){
// 递归左侧节点
node.left = remove(node.left, e);
return node;
}else if (e.compareTo(node.e) > 0){
// 递归右侧节点
node.right = remove(node.right, e);
return node;
}else {
// 删除最小值
if (node.left == null){
Node rightNode = node.right;
node.right = null;
size--;
return rightNode;
}
// 删除最大值
if (node.right == null){
Node leftNode = node.left;
node.left=null;
size--;
return leftNode;
}
//当前node有左右孩子,找出右树中的最小节点successor
Node successor = minimum(node.right);
// successor.right = 删除当前node最小值后的树
successor.right = removeMin(node.right);
// successor.left = 当前node节点的left
successor.left = node.left;
node.left = node.right = null;
// 返回successor
return successor;
}
}
public static void main(String[] args) {
_5_BinarySearchTree.BinarySearchTree<Integer> bst = new _5_BinarySearchTree.BinarySearchTree<>();
int[] nums = {28, 16, 30, 22, 13, 42, 29};
for (int num:nums){
bst.add(num);
}
System.out.println("-------前序遍历------");
bst.preOrder();
System.out.println("------中序遍历-------");
bst.inOrder();
System.out.println("------后序遍历-------");
bst.postOrder();
System.out.println("------广度优先遍历-------");
bst.levelOrder();
ArrayList<Integer> nums2 = new ArrayList<>();
//while(!bst.isEmpty())
// nums2.add(bst.removeMin());
//System.out.println(nums2);
//while(!bst.isEmpty())
// nums2.add(bst.removeMax());
//System.out.println(nums2);
//bst.remove(16);
//bst.preOrder();
}
}