java 二叉查找树和二分查找_java-二分查找树的实现

package com.learn.tree.demo2;

import java.util.LinkedList;

import java.util.Queue;

/**

* 二分查找树BST(也叫二叉查找树、二叉排序树)的提出是为了提供查找效率,

* 之所以称为二分查找树,因为该二叉树对应着二分查找算法,查找平均的时间复杂度为o(logn),所以该数据结构的提出是为了提高查找效率。 定义

* 二分查找树或者是一棵空树,或者具有下列性质: 1.若它的左子树不为空,则左子树上所有结点的值均小于根结点的值;

* 2.若它的右子树不为空,则右子树上所有结点的值均大于根结点的值; 3.它的左右子树均为二分查找树。

*

*

* 具体实现:二叉树的前序遍历,中序遍历,后序遍历,层次遍历 求二叉树深度(根到节点-叶子节点最长路径) 新增节点(没有该键 则新增 有则添加) 删除节点

*

* 注意:该二叉搜索树的实现是没有重复键值的

*

*/

public class BinaryTree, Value> {

private Node root;// 二叉树的根节点

private int count;// 二叉树中的节点数

// 二叉树的实例化 的构造器

public BinaryTree() {

root = null;// 初始化根节点

count = 0;

}

// 判断该 二叉树是否有节点

public boolean isEmpty() {

return count == 0;

}

// 该二叉树的节点数

public int size() {

return count;

}

// 新增二叉树的节点

public void insert(Key key, Value value) {

root = insert(root, key, value);

}

/**

* 插入节点的内部实现方法 采用递归的方法实现 二叉树的插入操作 返回 新增节点的二叉树的后根节点(为什么是根节点 注意递归后的的递进,还有回退 )

*

* @param root

* 该二叉树的根

* @param key

* @param value

*/

private Node insert(Node node, Key key, Value value) {

// 如果根节点的为空 ,则将新增的节点为 作为根节点

if (node == null) {

count++;

return new Node(key, value);

}

if (key.compareTo(node.key) > 0) {

node.right = insert(node.right, key, value);

} else if (key.compareTo(node.key) < 0) {

node.left = insert(node.left, key, value);

} else

node.value = value;

return node;

}

/**

* 获得最小节点

*

* @param key

*/

public Node getMininum() {

Node node = mininum(root);

return node;

}

/**

* 获得最小节点内部方法实现(没有左节点)--->迭代法

*

* @param node

* @param key

* @return

*/

private Node mininum(Node node) {

Node parent = null;

while (node != null) {

parent = node;

node = node.left;

}

return parent;

}

/**

* 获得最小节点内部方法实现(没有左节点)-->递归法

*

* @param node

* @return

*/

private Node minimumD(Node node) {

if (node.left== null)

return node;

return minimumD(node.left);

}

/**

* 获得的二叉树的最大节点

*

* @param key

* @return

*/

public Node getMaxinum() {

Node node = maxinum(root);

return node;

}

/**

* 最大节点的内部实现(没有右节点)--->迭代法

*

* @param node

* @param key

* @return

*/

private Node maxinum(Node node) {

Node parent = null;

while (node != null) {

parent = node;

node = node.right;

}

return parent;

}

/**

* 最大节点的内部实现(没有右节点)--->递归法

*

* @param node

* @return

*/

private Node maxinumD(Node node) {

if (node.right== null)

return node;

return maxinum(node.right);

}

/**

* 删除二叉搜索树的最小节点

*

* @return

*/

public Node removeMin() {

if (root != null)

root = removeMin(root);

return root;

}

/**

* 删除二叉搜索树的最小节点(不同的结构体 或者实体类实现方法不同) 这里是单项关联 过时将父节点的左节点 置为null,与左节点断开关联

* 返回删除节点后新的二分搜索树的根 ----------------------->递归法

*

* @param node

* @return

*/

private Node removeMin(Node node) {

if (node.left == null) {

final Node rightNode = node.right;

node.right = null;// let out gc

count--;

return rightNode;// 如果该节点有右节点,将该节点的 右节点 作为该

// 节点的父节点的左节点(于此同时也断开了该节点与父节点的联系)

}

// 递归调用左节点(直至左节点为空 删除该节点)

node.left = removeMin(node.left);

return node;

}

/**

* 删除二叉搜索树的最小节点--->迭代法

*

* @param node

*/

private void removeMinNode(Node node) {

Node parentNode = null;

while (node != null) {

parentNode = node;

node = node.left;

}

final Node rightNode = node.right;

node.right = null;// let out gc

parentNode.left = rightNode;

count--;

}

/**

* 删除二叉搜索树的最大节点

*

* @return

*/

public Node removeMax() {

if (root != null)

root = removeMax(root);

return root;

}

/**

* 删除二叉搜索树的最大节点(不同的结构体 或者实体类实现方法不同) 这里是单项关联 过时将父节点的右节点 置为null,与右节点断开关联

* 返回删除最大节点后的二叉搜索树的节点 ----------------------->递归法

*

* @param node

* @return

*/

private Node removeMax(Node node) {

if (node.right == null) {

final Node nodeLeft = node.left;

node.left = null;// let out gc

count--;

return nodeLeft;

}

node.right = removeMax(node.right);

return node;

}

/**

* 删除二叉搜索树的最大节点

*

* @param node

*/

private void removeMaxNode(Node node) {

Node parentNode = null;

while (node != null) {

parentNode = node;

node = node.right;

}

final Node nodeLeft = node.left;

node.left = null;// let out gc

node.right = nodeLeft;

count--;

}

/**

* 删除二叉树的节点 设p指向待删除的结点,pre指向待删除结点的父亲,则删除操作视如下情况而定:

* (1)若待删除的结点是叶子结点,不妨设pre->right=p(即待删除的结点为其父亲结点的右孩子),则直接删除p,对应为:pre->right=

* NULL,delete p;

* (2)若待删除的结点只有左子树或右子树,则只需将待删除结点的父亲与左孩子(或右孩子)连接起来,对应为,不妨设pre->right=p,

* 以待删除结点仅有左子树的情况为例(右子树同理),对应为:pre->right=p->left,delete p;

* (3)若待删除结点左右子树都有,则执行如下步骤:

* 总体来说,整个线索是:找到右子树的最小值结点-->连接断开结点-->对最小值结点的上下文做善后工作。

*

* @param key

*/

public void remove(Key key) {

remove(root, key);

}

/**

* 删除二叉树的节点 内部实现 使用递归的方法 返回删除后的二叉树的根节点--迭代法 返回该节点

*

* @param node

* @param key

* @return

*/

private Node remove(Node node, Key key) {

Node parent = root;

while (node != null) {

if (key.compareTo(node.key) > 0) {

parent = node;

node = node.right;

} else if (key.compareTo(node.key) < 0) {

parent = node;

node = node.left;

} else

break;// 当键相等的时候 就查到该节点 此时node就是 要 删除的节点,与parent 断开关联

}

if (node == null)

throw new RuntimeException("没有该键" + key + "对应的节点!");

// 要删除的左节点左节点不存在

if (node.left == null) {

final Node rihtNode = node.right;

if (node.key.compareTo(parent.key) > 0)

parent.right = rihtNode;

else

parent.left = rihtNode;

node.right = null;

count--;

return node;

}

// 要删除的右节点不存在(其中左右都为null不用考虑,处理包含在其中)

if (node.right == null) {

final Node leftNode = node.left;

if (node.key.compareTo(parent.key) > 0)

parent.left = leftNode;

else

parent.right = leftNode;

node.left = null;

count--;

return node;

}

// 要删除的左右节点都存在

if (node.right != null && node.left != null) {

// 找到该替换该节点的节点

Node successor = new Node(mininum(node.right));

successor.right = removeMin(node.right);

if (node.key.compareTo(parent.key) > 0)

parent.right = successor;

else

parent.left = successor;

successor.left = node.left;

node.left = null;// let out gc

node.right = null;// let out gc

return node;

}

return node;

}

/**

* 删除节点------->递归法 返回删除节点后二叉搜索树的根节点

*

* @param node

* @param key

* @return

*/

private Node removeNode(Node node, Key key) {

if (node == null)

return node;

if (key.compareTo(node.key) > 0) {

node.right = removeNode(node.right, key);

return node;

} else if (key.compareTo(node.key) < 0) {

node.left = removeNode(node.left, key);

return node;

} else {

if (node.left == null) {

final Node rNode = node.right;

node.right = null;

count--;

return rNode;

}

if (node.right == null) {

final Node lNode = node.left;

node.left = null;

count--;

return lNode;

}

if (node.right != null && node.left != null) {

Node successor = new Node(mininum(node.right));

successor.right = removeMin(node.right);

successor.left = node.left;

node.left = null;

node.right = null;

return successor;

}

return node;

}

}

/**

* 通过键 key获取节点

*

* @param key

* @return

*/

public Node node(Key key) {

Node node = node(root, key);

return node;

}

/**

* 用迭代法实现-通过键是获取节点

*

* @param node

* @param key

* @return

*/

private Node node(Node node, Key key) {

while (node != null) {

if (key.compareTo(node.key) > 0)

node = node.right;

else if (key.compareTo(node.key) < 0)

node = node.left;

else

return node;

}

return null;

}

/**

* 判断该键是否在 二叉搜索树

*

* @param key

* @return

*/

public boolean contain(Key key) {

return contain(root, key);

}

/**

* 递归法

*

* @param node

* @param key

* @return

*/

private boolean contain(Node node, Key key) {

if (node == null)

return false;

if (key.compareTo(node.key) == 0)

return false;

else if (key.compareTo(node.key) > 0)

return contain(node.right, key);

else

return contain(node.left, key);

}

/**

* 前序遍历(深度遍历的一种) Traversal

*/

public void preOrder() {

if (root != null)

preOrder(root);

}

/**

* 满足 根-->左--->右 (递归法)

*

* @param node

*/

private void preOrder(Node node) {

if (node != null)

return;

System.out.println("节点键的为:" + node.key + " 节点的值为:" + node.value);

preOrder(node.left);

preOrder(node.right);

}

/**

* 中序遍历(深度遍历的一种)Traversal 中序遍历二叉搜索树 满足从小到大顺序 (排序)

*/

public void inOrder() {

if (root != null)

inOrder(root);

}

/**

* 中序遍历 --->递归法

*

* @param node

*/

private void inOrder(Node node) {

if (node == null)

return;

inOrder(node.left);

System.out.println("节点键的为:" + node.key + " 节点的值为:" + node.value);

inOrder(node.right);

}

/**

* 后序遍历(深度遍历的一种)Traversal

*/

public void postOrder() {

if (root != null)

postOrder(root);

}

/**

* 中序遍历 --->递归法

*

* @param node

*/

private void postOrder(Node node) {

if (node == null)

return;

postOrder(node.left);

postOrder(node.right);

System.out.println("节点键的为:" + node.key + " 节点的值为:" + node.value);

}

/**

* 层序遍历(广度优先遍历)

*

* @param node

*/

public void levelOrder() {

if (root != null)

levelOrder(root);

}

/**

* 利用队列 (Queue)先进先出

*

* @param node

*/

private void levelOrder(Node node) {

Queue queue = new LinkedList();

queue.offer(node);

while (!queue.isEmpty()) {

Node element = queue.poll();

System.out.println("节点键的为:" + element.key + " 节点的值为:" + element.value);

if (element.left != null)

queue.offer(element.left);

if (element.right != null)

queue.offer(element.right);

}

}

/**

* 二叉树的节点用一个内部类来实现

*

* @author caibixiang

*

*/

private class Node {

private Key key;// 键 (满足根节点的键小于右节点中所有节点,大于左节点的中所有节点)

private Value value;// 值

private Node left;// 左节点

private Node right;// 又节点

private Node(Key key, Value value) {

this.key = key;

this.value = value;

this.left = null;

this.right = null;

}

private Node(Node node) {

this.key = node.key;

this.value = node.value;

this.left = node.left;

this.right = node.right;

}

}

public static void main(String[] args) {

int N = 100;

// 创建一个数组,包含[0....N]的所有的元素

Integer[] arr = new Integer[N];

for (int i = 0; i < N; i++)

arr[i] = new Integer(i);

// 打乱数组顺序

for (int i = 0; i < N; i++) {

int pos = (int) (Math.random() * (i + 1));

Integer t = arr[pos];

arr[pos] = arr[i];

arr[i] = t;

}

// 由于我们实现的二分搜索树不是平衡二叉树

// 所以如果按照顺序插入一组数据,我们的二分搜索树会退化成为一个链表

// 平横二叉树的实现下一节来实现(avl树)

BinaryTree bst = new BinaryTree();

for (int i = 0; i < N; i++)

bst.insert(new Integer(arr[i]), Integer.toString(arr[i]));

bst.inOrder();// 升序排列

bst.remove(5);

bst.remove(7);

bst.remove(71);

bst.remove(23);

bst.remove(34);

bst.remove(56);

System.out.println(bst.size());

bst.inOrder();// 升序排列

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值