二叉搜索 java_数据结构之二叉搜索树—Java实现

二叉查找树(Binary Search Tree,简称BST)是一棵二叉树,它的左子节点的值比父节点的值要小,右节点的值要比父节点的值大。它的高度决定了它的查找效率。

在理想的情况下,二叉查找树增删查改的时间复杂度为O(logN)(其中N为节点数),最坏的情况下为O(N)。当它的高度为logN+1时,我们就说二叉查找树是平衡的。b45eab5c1647a25ae159830717af8e03.png

首先定义二叉树节点

class TreeNode{

TreeNode(int value) {

this.value = value;

}

TreeNode left;

TreeNode right;

int value;

}

复制代码

本文中rootNode为创建二叉搜索树的root节点,在类初始化阶段赋值。

private final TreeNode rootNode;

public BinarySearchTree(int value){

rootNode = new TreeNode(value);

}

复制代码

插入操作:

首先从root节点开始循环遍历,

如果插入节点等于当前root节点,直接结束不做任何处理。

如果插入节点小于当前root节点,从左节点开始查找。 (root=root.left)

如果插入节点大于当前root节点,从右节点开始查找。 (root=root.right)

当当前节点的左节点或者右节点为空时,插入新节点,结束过程。

上代码:

public TreeNode insert(int value){

TreeNode currentRoot = rootNode;

TreeNode newNode = new TreeNode(value);

while (true) {

if (value == currentRoot.value) {

return null;

}

else if (value < currentRoot.value) {

if (currentRoot.left != null) {

currentRoot = currentRoot.left;

}

else {

return currentRoot.left = newNode;

}

}

else {

if (currentRoot.right != null) {

currentRoot = currentRoot.right;

}

else {

return currentRoot.right = newNode;

}

}

}

}

复制代码

查找操作:

类似于插入操作:

如果插入节点等于root节点,查找完成。

如果插入节点小于root节点,从left节点开始查找。 (root=root.left)

如果插入节点大于root节点,从right节点开始查找。(root=root.right)

上代码:

public TreeNode get(int value){

TreeNode currentRoot = rootNode;

while (true) {

if (currentRoot == null) {

return null;

}

if (value == currentRoot.value) {

return currentRoot;

}

else if (value < currentRoot.value) {

currentRoot = currentRoot.left;

}

else {

currentRoot = currentRoot.right;

}

}

}

复制代码

遍历操作:

遍历分为

前序:根节点放在左节点的左边        (根→左→右)

中序:根节点放在左节点和右节点的中间(左→根→右)

后序:根节点放在右节点的右边        (左→右→根)

中序遍历的代码:

public void getTreeByInOrder(List list, TreeNode root){

if (root != null) {

getTreeByInOrder(list, root.left);

list.add(root);

getTreeByInOrder(list, root.right);

}

}

复制代码

前序后序和其类似,就不贴代码了

层次遍历(也可以叫做广度优先遍历):

思路:

由根从上往下循环遍历,利用队列(queue)先进先出的原则,每访问一个节点之后,将其左右子节点入队。这样,同一层的所有节点肯定先于下一层的节点先访问

如果需要分层保存节点呢?(比如需要换行打印):问题的难点是如何知道本层已经遍历完了,可以使用两个指针last和nextLast,一个指向当前层的最后一个节点,一个指向下一层的最后一个节点,这样难点转换成了last和nextLast的更新。当节点入队时,更新nextLast(设置nextLast为队列的最后一个节点),因为队列中的最后一个节点一定是下一层的最后节点。

上代码:

public List> printTreeByHierarchy() {

TreeNode currentRoot = rootNode;

Queue queue = new LinkedList<>();

TreeNode last = currentRoot;

TreeNode nextLast = null;

queue.offer(currentRoot);

List> lists = new ArrayList<>();

List nodes = new ArrayList<>();

while (!queue.isEmpty()) {

currentRoot = queue.poll();

nodes.add(currentRoot.value);

if (currentRoot.left != null) {

queue.offer(currentRoot.left);

nextLast = currentRoot.left;

}

if (currentRoot.right != null) {

queue.offer(currentRoot.right);

nextLast = currentRoot.right;

}

if (last == currentRoot) {//因为last是当前层的最后一个节点,如果等式成立,说明这层已经遍历完

last = nextLast;

lists.add(nodes);

nodes = new ArrayList<>();

}

}

System.out.println(lists);

return lists;

}

复制代码

删除操作:

6e6dd0b3780c2ef3e99495eb893431b5.png

删除操作比较麻烦,需要分4种情况考虑。我们以上面这颗树作为参考:

如果删除节点含有左右子节点:需要用删除节点的后继节点(以中序遍历)取代删除节点的位置。----比如节点删除20,需要用26代替20的位置

如果删除节点只含有单个节点(或左或右):需要用删除节点的左(右)节点取代删除节点的位置。----比如节点删除27,需要用26代替27的位置

如果删除节点无子节点(是叶子节点):直接删除。

上代码:

public void remove(int value){

TreeNode currentRoot = rootNode;

while (true) {

if (currentRoot == null) {

return;//没找到,返回或者抛异常

}

if (value < currentRoot.value) {

currentRoot = currentRoot.left;

}

else if (value > currentRoot.value) {

currentRoot = currentRoot.right;

}

else {

TreeNode replacement;//代替者

if (currentRoot.left == null && currentRoot.right == null) {//无双子节点

resetParentNode(currentRoot, null);

}

else if (currentRoot.left != null && currentRoot.right != null) {//双子节点

replacement = getSuccessor(currentRoot);

replacement.left = currentRoot.left;

replacement.right = currentRoot.right;

resetParentNode(replacement, null); //重设后继节点的父节点

resetParentNode(currentRoot, replacement);//重设正在移除节点的父节点

}

else if (currentRoot.left != null) {

resetParentNode(currentRoot, currentRoot.left); //

} //

else { //单节点

resetParentNode(currentRoot, currentRoot.right); //

} //

return;

}

}

}

复制代码

如果文章中有分析错误的地方或者值得改进的地方,欢迎大家指出。

参考文章:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值