算法导论读书笔记(14) - 二叉查找树的具体实现

算法导论读书笔记(14) - 二叉查找树的具体实现

目录

  • 二叉查找树的简单Java实现

二叉查找树的简单Java实现

/**
* 二叉查找树
* 部分代码参考自TreeMap的源码
*/
public class BinarySearchTree<T> {
protected TreeNode<T> root = null;
private final Comparator<? super T> comparator;
private int size = 0;

public BinarySearchTree() {
comparator = null;
}

public BinarySearchTree(Comparator<? super T> comparator) {
this.comparator = comparator;
}

/**
* 向树中插入新结点
*
* @param key 新结点的key值
*/
public void insert(T key) {
TreeNode<T> x = root;
TreeNode<T> y = null;
TreeNode<T> z = new TreeNode<T>(key, null);
int cmp;
if (x == null) { // 如果根结点是null,则新插入结点成为根结点
root = z;
size = 1;
return;
}
while (x != null) { // x从根结点开始沿树下降,直到找到z应当插入的位置。y用于找出z的父结点的位置
y = x;
cmp = compareKey(z.key, x.key);
if (cmp < 0)
x = x.left;
else
x = x.right;
}
z.parent = y;
cmp = compareKey(z.key, y.key);
if (cmp < 0)
y.left = z;
else
y.right = z;
size++;
}

/**
* 删除树中某个结点
*
* @param key 待删除结点的key值
* @return 被删除结点的key值
*/
public T remove(T key) {
TreeNode<T> p = find(key);
if (p == null)
return null;
T oldValue = p.key;
deleteNode(p);
return oldValue;
}

public boolean isEmpty() {
return size() == 0;
}

public int size() {
return size;
}

/**
* 返回二叉查找树中最左子结点
*
* @return 返回二叉查找树中最左子结点
*/
public TreeNode<T> firstNode() {
return getFirstNode(root);
}

/**
* 返回二叉查找树中最右子结点
*
* @return 返回二叉查找树中最右子结点
*/
public TreeNode<T> lastNode() {
return getLastNode(root);
}

/**
* 根据key值查找结点
*/
public TreeNode<T> find(T t) {
TreeNode<T> p = root;
while (p != null) {
int cmp = compareKey(t, p.key);
if (cmp < 0)
p = p.left;
else if (cmp > 0)
p = p.right;
else
return p;
}
return null;
}

/**
* 返回结点集
*/
public Set<TreeNode<T>> nodeSet() {
return new NodeSet();
}

/**
* 返回传入结点的后继结点
*/
private static <T> TreeNode<T> successor(TreeNode<T> t) {
if (t == null)
return null;
else if (t.right != null) // 如果t的右子树不空,那么t的后继就是其右子树中key值最小的结点
return getFirstNode(t.right);
else { // 如果t的右子树为空,左子树不空,那么t的后继就是t的一个最低祖先结点,且该结点的左孩子也必须是t的祖先结点
TreeNode<T> p = t.parent;
TreeNode<T> ch = t;
while (p != null && ch == p.right) {
ch = p;
p = p.parent;
}
return p;
}
}

/**
* 返回传入结点的前趋结点
*/
private static <T> TreeNode<T> predecessor(TreeNode<T> t) {
if (t == null)
return null;
else if (t.left != null)
return getLastNode(t.left);
else {
TreeNode<T> p = t.parent;
TreeNode<T> ch = t;
while (p != null && ch == p.left) {
ch = p;
p = p.parent;
}
return p;
}
}

private static <T> TreeNode<T> getFirstNode(TreeNode<T> t) {
TreeNode<T> p = t;
if (p != null)
while (p.left != null)
p = p.left;
return p;
}

private static <T> TreeNode<T> getLastNode(TreeNode<T> t) {
TreeNode<T> p = t;
if (p != null)
while (p.right != null)
p = p.right;
return p;
}

/**
* 用结点v替换结点u在树中的位置
*/
private void transplant(TreeNode<T> u, TreeNode<T> v) {
if (u.parent == null)
root = v;
else if (u == u.parent.left)
u.parent.left = v;
else
u.parent.right = v;
if (v != null)
v.parent = u.parent;
}

/**
* 删除树中的结点
*/
private void deleteNode(TreeNode<T> p) {
size--;
if (p.left == null) // 如果结点的左子树为空,则用右子树替换结点
transplant(p, p.right);
else if (p.right == null) // 如果结点的右子树为空,则用左子树替换结点
transplant(p, p.left);
else { // 结点有两个子结点
TreeNode<T> y = getFirstNode(p.right); // 首先找出结点p的后继y
if (y.parent != p) { // 如果p不是y的父结点,那么需要用y的右孩子替换y
transplant(y, y.right);
y.right = p.right;
y.right.parent = y;
}
transplant(p, y); // 最后用y替换p
y.left = p.left;
y.left.parent = y;
}
}

/**
* 用于比较树中结点的key值
*/
private int compareKey(T key1, T key2) {
int cmp;
if (comparator != null)
cmp = comparator.compare(key1, key2);
else {
if (key1 == null || key2 == null)
throw new NullPointerException();
Comparable<? super T> k = (Comparable<? super T>) key1;
cmp = k.compareTo(key2);
}
return cmp;
}

private static boolean keyEquals(Object o1, Object o2) {
return (o1 == null ? o2 == null : o1.equals(o2));
}

/**
* 二叉查找树的结点类
*/
public static final class TreeNode<T> {
T key;
TreeNode<T> left = null;
TreeNode<T> right = null;
TreeNode<T> parent;

TreeNode(T key, TreeNode<T> parent) {
this.key = key;
this.parent = parent;
}

public T getKey() {
return key;
}

public boolean equals(Object o) {
if (!(o instanceof TreeNode))
return false;
TreeNode<?> e = (TreeNode<?>) o;
return keyEquals(key, e.getKey());
}

public String toString() {
return "[" + key + "]";
}
}

/**
* 树中结点的集合类,使用迭代器遍历,从最左结点开始,依次取其后继结点(即中序遍历)
*/
final class NodeSet extends AbstractSet<TreeNode<T>> {
public Iterator<TreeNode<T>> iterator() {
return new NodeIterator(firstNode());
}

public int size() {
return BinarySearchTree.this.size();
}
}

final class NodeIterator extends PrivateNodeIterator<TreeNode<T>> {
NodeIterator(TreeNode<T> first) {
super(first);
}

public TreeNode<T> next() {
return nextNode();
}

public void remove() { }
}

abstract class PrivateNodeIterator<E> implements Iterator<E> {
TreeNode<T> next;

PrivateNodeIterator(TreeNode<T> first) {
next = first;
}

public boolean hasNext() {
return next != null;
}

final TreeNode<T> nextNode() {
TreeNode<T> e = next;
if (e == null)
throw new NoSuchElementException();
next = successor(e);
return e;
}
}
}

算法导论读书笔记(14) - 二叉查找树的具体实现,布布扣,bubuko.com

时间: 2024-10-05 23:27:04

算法导论读书笔记(14) - 二叉查找树的具体实现的相关文章

算法导论读书笔记(13)

算法导论读书笔记(13) 目录 红黑树 旋转 插入 情况1 : z 的叔父结点 y 是红色的 情况2 : z 的叔父结点 y 是黑色的,而且 z 是右孩子 情况3 : z 的叔父结点 y 是黑色的,而且 z 是左孩子 删除 情况1 : x 的兄弟 w 是红色的 情况2 : x 的兄弟 w 是黑色的,且 w 的两个孩子都是黑色的 情况3 : x 的兄弟 w 是黑色的, w 的左孩子是红色的,右孩子是黑色的 情况4 : x 的兄弟 w 是黑色的,且 w 的右孩子是红色的 红黑树 红黑树 是一种二叉查

算法导论读书笔记之钢条切割问题

算法导论读书笔记之钢条切割问题 巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 给定一段长度为n英寸的钢条和一个价格表 pi (i=1,2, -,n),求切割钢条的方案,使得销售收益rn最大.注意,如果长度为n英寸的钢条价格pn足够大,最优解可能就是完全不需要切割. 若钢条的长度为i,则钢条的价格为Pi,如何对给定长度的钢条进行切割能得到最大收益? 长度i   1   2    3   4     5      6     7     8  

算法导论读书笔记(18)

算法导论读书笔记(18) 目录 最长公共子序列 步骤1:描述最长公共子序列的特征 步骤2:一个递归解 步骤3:计算LCS的长度 步骤4:构造LCS LCS问题的简单Java实现 最长公共子序列 某给定序列的子序列,就是将给定序列中零个或多个元素去掉后得到的结果.其形式化定义如下:给定一个序列 X = < x1 , x2 , - , xm >,另一个序列 Z = < z1 , z2 , - , zk >,如果 Z 满足如下条件则称 Z 为 X 的 子序列 (subsequence),

算法导论读书笔记(15) - 红黑树的具体实现

算法导论读书笔记(15) - 红黑树的具体实现 目录 红黑树的简单Java实现 红黑树的简单Java实现 /** * 红黑树 * * 部分代码参考自TreeMap源码 */ public class RedBlackTree<T> { protected TreeNode<T> root = null; private final Comparator<? super T> comparator; private int size = 0; private static

算法导论读书笔记(17)

算法导论读书笔记(17) 目录 动态规划概述 钢条切割 自顶向下的递归实现 使用动态规划解决钢条切割问题 子问题图 重构解 钢条切割问题的简单Java实现 动态规划概述 和分治法一样, 动态规划 (dynamic programming)是通过组合子问题的解而解决整个问题的.分治法是将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解.与此不同,动态规划适用于子问题并不独立的情况,即各子问题包含公共的子子问题.在这种情况下,分治法会重复地求解公共的子子问题.而动态

算法导论读书笔记(16)

算法导论读书笔记(16) 目录 动态顺序统计 检索具有给定排序的元素 确定一个元素的秩 区间树 步骤1:基础数据结构 步骤2:附加信息 步骤3:维护信息 步骤4:设计新操作 动态顺序统计 之前介绍过 顺序统计 的概念.在一个无序的集合中,任意的顺序统计量都可以在 O ( n )时间内找到.而这里我们将介绍如何在 O ( lg n )时间内确定任意的顺序统计量. 下图显示的是一种支持快速顺序统计量操作的数据结构.一棵 顺序统计树 T 通过在红黑树的每个结点中存入附加信息而成.在一个结点 x 内,增

算法导论读书笔记-第十四章-数据结构的扩张

算法导论第14章 数据结构的扩张 一些工程应用需要的只是标准数据结构, 但也有许多其他的应用需要对现有数据结构进行少许的创新和改造, 但是只在很少情况下需要创造出全新类型的数据结构, 更经常的是通过存储额外信息的方法来扩张一种标准的数据结构, 然后对这种数据结构编写新的操作来支持所需要的应用. 但是对数据结构的扩张并不总是简单直接的, 因为新的信息必须要能被该数据结构上的常规操作更新和维护. 14.1 动态顺序统计 顺序统计树(order-static tree) : 在红黑树的基础上, 在每个

平摊分析 --- 算法导论读书笔记

我们经常会说一个算法快不快,这个可以由实验得出,也可以通过分析复杂度得出.实验需要大量不同的输入才更全面准确,否则片面地看某个输入下的表现,是比较偏颇的.分析复杂度(通常分析最坏,因为平均涉及输入的概率分布,依靠假设或者实验和经验)有时候并不是一个简单的事,简单的情况是遍历 for(int i = 0; i != n; i++) 的这种情况,显然是O(n)的复杂度.但是一些复杂的情况就比较难办了,举例来说: a.   栈操作:  除了PUSH,POP,添加一个操作叫MULTIPOP. MULTI

字符串匹配问题 ---- 算法导论读书笔记

字符串匹配是一个很常见的问题,可以扩展为模式的识别,解决字符串问题的思想被广泛地应用.介绍四种解决该问题的办法,包括:最朴素的遍历法,Rabin-Karp算法,自动机机匹配,Knuth-Morris-Pratt算法即耳熟能详的KMP. 在一开始,先对时间复杂度做出一个总扩(从大到小):[1]朴素法:O( (n-m+1)m ):[2]Rabin-Karp:预处理:O(m),匹配:最坏O( (n-m+1)m ),但是平均和实际中比这个好得多:[3]自动机:预处理O(m|Σ|),匹配O(n):[4]K