算法系列(八)数据结构之二叉查找树
来源:互联网 发布:linux使用 编辑:程序博客网 时间:2024/05/01 16:26
在算法系列(七)数据结构之树的基本结构和二叉树的遍历 中介绍了基本的树结构,二叉树的实现和遍历。
这篇文章重点学习一下二叉查找树。
概述
二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree)二叉搜索树。
二叉查找树(BST)是二叉树的一个重要的应用,它在二叉树的基础上加上了这样的一个性质:对于树中的每一个节点来说,如果有左儿子的话,它的左儿子的值一定小于它本身的值,如果有右儿子的话,它的右儿子的值一定大于它本身的值。二叉查找树的操作一般有插入、删除和查找,这几个操作的平均时间复杂度都为O(logn),插入和查找操作很简单,删除操作会复杂一点。
二叉查找树(BST)是二叉树的一个重要的应用,它在二叉树的基础上加上了这样的一个性质:对于树中的每一个节点来说,如果有左儿子的话,它的左儿子的值一定小于它本身的值,如果有右儿子的话,它的右儿子的值一定大于它本身的值。二叉查找树的操作一般有插入、删除和查找,这几个操作的平均时间复杂度都为O(logn),插入和查找操作很简单,删除操作会复杂一点。
由于树的递归定义,通常用递归实现一些操作,二叉查找树平均深度是O(logN),不必担心栈溢出。
定义
二叉查找树是一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉查找树;
(4)没有键值相等的结点。
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉查找树;
(4)没有键值相等的结点。
代码实现基础
继承了上一篇文章中的树结构,增加了比较器。必须有明确的节点比较方法,否则无法进行基本操作。
package com.algorithm.tree;import java.util.Comparator;/** * 左子树小于右子树的二叉查找树 * * @author chao * * @param <T> */public class BinarySeachTree<T> extends BinaryTree<T> {private Comparator<T> comparator;public BinarySeachTree(BinarySeachTree.BinaryTreeNode<T> root) {this(root, null);}public BinarySeachTree(BinarySeachTree.BinaryTreeNode<T> root, Comparator<T> comparator) {super(root);this.comparator = comparator;}private int compare(BinarySeachTree.BinaryTreeNode<T> a, BinarySeachTree.BinaryTreeNode<T> b) {if (comparator != null) {return comparator.compare(a.element, b.element);} else if (a.element instanceof Comparable && b.element instanceof Comparable) {return ((Comparable) a.element).compareTo(b.element);} else {throw new RuntimeException("can't compare " + a.element.getClass());}}}
插入操作
步骤:将X节点插入到树T中,当前比较节点为cur,插入节点为x,r。默认从根节点为初始节点开始比较,cur初始值为root,如果根节点为空,将x直接作为根节点。否则比较cur与x的大小,如果x比cur大,说明x在cur的右子树上,以cur.right为初始节点,继续做相同的操作。如果cur比x大,说明x在cur的左子树上,以cur.left为初始节点,继续做相同操作。
代码实现
/** * 从根节点插入一个新数据 * * @param x * @return */public BinaryTreeNode<T> insert(BinaryTreeNode<T> x) {return insert(root, x);}/** * 从某一个节点插入新数据 * * @param cur * @param x * @return */protected BinaryTreeNode<T> insert(BinaryTreeNode<T> cur, BinaryTreeNode<T> x) {if (cur == null) {return x;}int compareresult = compare(cur, x);if (compareresult < 0) {cur.right = insert(cur.right, x);} else if (compareresult > 0) {cur.left = insert(cur.left, x);}return cur;}
查找最大值与最小值
根据定义可以知道,最小值位于最左侧的叶子节点。最大值位于最右侧的叶子节点。
代码实现
/** * 查找最小节点 * * @return */public BinaryTreeNode<T> findMin() {return findMin(root);}/** * 以node为根节点的最小节点 * * @param node * @return */public BinaryTreeNode<T> findMin(BinaryTreeNode<T> node) {if (node == null) {return null;} else if (node.left == null) {return node;}return findMin(node.left);}/** * 查找最大节点 * * @return */public BinaryTreeNode<T> findMax() {return findMax(root);}/** * 以node为根节点的最大节点 * * @param node * @return */public BinaryTreeNode<T> findMax(BinaryTreeNode<T> node) {if (node == null) {return null;} else if (node.right == null) {return node;}return findMax(node.right);}
查找是否包含一个节点的操作
有点二分查找的意思。只是二分查找是线性的,查找树的查找是树形结构上的查找。
代码实现:
/** * 判断是否包含x * * @param x * @return */public boolean contains(BinaryTreeNode<T> x) {return contains(root, x);}/** * 以某个节点为根节点判断是否包含x * * @param cur * @param x * @return */public boolean contains(BinaryTreeNode<T> cur, BinaryTreeNode<T> x) {if (x == null || cur == null) {return false;}int compareresult = compare(cur, x);if (compareresult == 0) {return true;} else if (compareresult < 0) {return contains(cur.right, x);} else {return contains(cur.left, x);}}
删除操作
删除操作需要分情况讨论:
如果是叶子节点,可以直接删除。
如果只有一个孩子的话,就让它的父亲指向它的儿子,然后删除这个节点。
如果有两个孩子的话,一般的删除策略是用右子树最小的数据代替该节点的数据,递归删除那个节点。
代码实现
/** * 删除节点x * * @param x * @return */public BinaryTreeNode<T> remove(BinaryTreeNode<T> x) {return remove(root, x);}/** * 删除子树中的节点x * * @param cur * @param x * @return */public BinaryTreeNode<T> remove(BinaryTreeNode<T> cur, BinaryTreeNode<T> x) {if (cur == null || x == null) {return cur;}int compareresult = compare(cur, x);if (compareresult > 0) {cur.left = remove(cur.left, x);} else if (compareresult < 0) {cur.right = remove(cur.right, x);} else if (cur.left != null && cur.right != null) {BinaryTreeNode<T> minnode = findMin(cur.right);cur.right = remove(cur.right, minnode);cur.element = minnode.element;minnode = null;} else {cur = (cur.left == null) ? cur.right : cur.left;}return cur;}
删除的过程比较复杂,如果删除次数不多,通常使用的策略是惰性删除,即对删除的节点进行标记。特别是有重复项时非常有用,删除只是项的频率减1。暂时不做
代码实现可以看github,地址https://github.com/robertjc/simplealgorithm
github代码也在不断完善中,有些地方可能有问题,还请多指教
欢迎扫描二维码,关注公众账号
0 0
- 算法系列(八)数据结构之二叉查找树
- 数据结构查找算法之二叉查找树
- 数据结构与算法之八(二叉树)
- <数据结构与算法>之二叉查找树
- 算法导论(八)二叉查找树
- 排序算法系列之二叉查找树
- [算法系列之六]二叉查找树
- 数据结构和算法系列之 二叉树
- 算法学习之数据结构之二叉查找树
- Javascript数据结构算法之二叉查找树BST(构造,遍历,查找,删除,计数)
- 数据结构之三(二叉查找树)
- 数据结构(五)之二叉查找树
- 算法导论学习笔记(八):二叉查找树
- 数据结构之二叉查找树
- 数据结构之二叉查找树
- 数据结构之查找二叉树
- 数据结构之二叉查找树
- 数据结构之二叉查找树
- ACM:蓝桥杯:斐波那契数
- 通过Activity设置动态Fragment中的ListView(通过SimpleCursorAdapter适配)
- 小白看 KMP算法
- hdu2795Billboard
- Netbeans8.1 IDE的插件和历史记录(二)
- 算法系列(八)数据结构之二叉查找树
- Delete Node in a Linked List
- ACM:蓝桥杯:韩信点兵
- 老照片
- ACM:蓝桥杯:水仙花数
- 2实验吧逆向
- 进程与线程关系
- json解读以及Gson的使用
- ITQ(Iterative Quantization)迭代量化方法详解 hash 哈希算法