java 二叉树 红黑树_常见数据结构(二)-树(二叉树,红黑树,B树)

常见数据结构(二)-树(二叉树,红黑树,B树)

标签: algorithms

[TOC]

本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树

写在前面

本文所有图片均截图自coursera上普林斯顿的课程《Algorithms, Part I》中的Slides

相关命题的证明可参考《算法(第4版)》

源码可在官网下载,也可以在我的github仓库 algorithms-learning下载,已经使用maven构建

仓库下载:git clone git@github.com:brianway/algorithms-learning.git

Binary Search Tree(二分查找树)

定义:A BST is a binary tree in symmetric order.

A binary tree is either:

Empty.

Two disjoint binary trees (left and right).

Symmetric order.Each node has a key, and every node’s key is:

Larger than all keys in its left subtree.

Smaller than all keys in its right subtree.

在java的实现中,每个节点(Node)由四个域组成:key,value,left,right。即:键,值,左子树,右子树。

private class Node {

private Key key;

private Value val;

private Node left, right;

public Node(Key key, Value val) {

this.key = key;

this.val = val;

}

}

7ebec4cf90d20cf7aee84109340347bc.png

查找:得到相应键的值,若无此键则返回null.

/* 查找 */

public Value get(Key key) {

Node x = root;

while (x != null) {

int cmp = key.compareTo(x.key);

if (cmp < 0) {

x = x.left;

} else if (cmp > 0) {

x = x.right;

} else { // if (cmp == 0)

return x.val;

}

}

return null;

}

插入:如果小,往左;如果大,往右;如果null,插入;如果存在,覆盖。

/* 插入 */

public void put(Key key, Value val) {

root = put(root, key, val);

}

/* 辅助函数,递归调用 */

private Node put(Node x, Key key, Value val) {

if (x == null) return new Node(key, val);

int cmp = key.compareTo(x.key);

if (cmp < 0) {

x.left = put(x.left, key, val);

} else if (cmp > 0) {

x.right = put(x.right, key, val);

} else { // if (cmp == 0)

x.val = val;

}

return x;

}

比较的次数为节点的深度+1,由于插入节点的顺序会有差异,所以树的高度不确定,最坏的情况是N个节点的树高度为N。

删除:列出下面几种处理方法

将值置为null,在树中保留键

删除最小值:一直向左找到左子树为null的节点,用它的右子节点代替它。

Hibbard deletion

下面重点讲一下Hibbard deletion,分为三种情况:

没有子节点的节点,将其parent link置为null即可。

有一个子节点的节点,删除该节点并以子节点代替即可。

有两个子节点的节点,找到该节点t的下一个节点x(即右子树的最小节点),在右子树删除这个节点,并将该节点x放到t的位置。

/* 删除 */

private Node delete(Node x, Key key) {

if (x == null) return null;

int cmp = key.compareTo(x.key);

if (cmp < 0) {

x.left = delete(x.left, key);

} else if (cmp > 0) {

x.right = delete(x.right, key);

} else {

if (x.right == null) return x.left; // no right child

if (x.left == null) return x.right; // no left child

Node t = x;

x = min(t.right); // replace with successor

x.right = deleteMin(t.right);

x.left = t.left;

}

x.count = size(x.left) + size(x.right) + 1;

return x;

}

2-3 Search Trees(2-3树)

在介绍红黑树前,先介绍一下2-3树,便于后面理解红黑树。

2-3树是二分查找树的变形,每个节点是下面两种情况之一:

2-node:一个键,两个分叉(smaller,larger)

3-node:两个键,三个分叉(smaller,between,larger)

f2ddbb1828002603d6fab87d070d95cb.png

在底部向一个3-node插入。

向3-node插入一个键,临时成为一个4-node

将4-node中间的key移动到父节点

向上重复

如果到了顶端的根节点,且根节点是4-node,将其分成3个2-nodes.

总结起来就是:当插入的值导致节点变四叉时进行分裂,将中间的值传给上一个节点,并将另外两个值作为两个子节点分开,若上一节点也因此变成四叉,依次类推。分裂4-node是一个local transformation,只会进行常数次数的操作。高度加一由且仅由顶节点分裂造成

cea31da02aa32337dc1bee60aefca1eb.png

树的高度,在查找和插入时,保证了logarithmic的性能。

Worst case: lg N. [all 2-nodes]

Best case: log3 N ≈ 0.631 lg N. [all 3-nodes]

Red-Black BSTs(红黑树)

这里的红黑树均指Left-leaning red-black BSTs。主要是用二叉树的形式来表示2-3树,用一个“内部”的left-leaning连接来表示3-node。red link是2-3tree的三叉节点的连接两个key的内部link,大值作为根节点,小值作为左子节点,故名left leaning 红黑树。

8077fe44faafaf5523774fa1c0260b08.png

一个等价的定义,A BST such that:

No node has two red links connected to it.

Every path from root to null link has the same number of black links.

Red links lean left.

a29d608ab0be214a4bc17005b885fbf7.png

红黑树的java表示

private static final boolean RED = true;

private static final boolean BLACK = false;

private class Node {

Key key;

Value val;

Node left, right;

boolean color;// color of parent link

}

private boolean isRed(Node x) {

if (x == null) return false;

return x.color == RED;

}

左转-右转-变色

红黑树插入过程中可能用到的三个基本操作(左转,右转,变色):

left rotate

right rotate

flip colors

下面依次介绍

左转

289269f00eb6aa00ae8b6bf0f24452de.png

/* left rotate */

private Node rotateLeft(Node h) {

assert isRed(h.right);

Node x = h.right;

h.right = x.left;

x.left = h;

x.color = h.color;

h.color = RED;

return x;

}

右转

94d56db96f8067fba45a1b01590a2001.png

/* right rotate */

private Node rotateRight(Node h) {

assert isRed(h.left);

Node x = h.left;

h.left = x.right;

x.right = h;

x.color = h.color;

h.color = RED;

return x;

}

变色

442cc5aa21d8c246566e6b30f41d4127.png

/* flip colors */

private void flipColors(Node h) {

assert !isRed(h);

assert isRed(h.left);

assert isRed(h.right);

h.color = RED;

h.left.color = BLACK;

h.right.color = BLACK;

}

插入操作

2ce4c9b44dbf35fa125d3ae23ab5fa53.png

从图中可以看出,插入的次序不同,需要转换的操作也不同,分三种情况(图中每一列是一种情况):

已有a和b时,c插入在b的右子节点,直接变色即可

已有b和c时,a插入在b的左子节点,先右转把b滑上去,成1中的状态,再变色即可

已有a和c时,b插入在a的右子节点,先左转把a滑下去,成2中的状态,再右转+变色即可

从上面的分析可以看出,三种情况之间有转换关系,且逐步趋向简单,如下图所示:

f4a832e049125c7eb2177dd8c211351e.png

根本原因在于,2-3树中,是把3-node中处于中间的那个键传递给父节点,所以在红黑树中,当有一个节点连了两个 red link时,说明这三个点是一个3-node,但次序还需要调整,从而达到中间键在最上的状态,进而变色。而这个这个调整的趋势则是先让b处于a,c中间(即a的父,c的左子,成一条线),再让b成为a,c的父节点,最后变色。记住这个顺序和原因,写代码就简单了,状态3->状态2->状态1

private Node put(Node h, Key key, Value val) {

//insert at bottom (and color it red)

if (h == null) return new Node(key, val, RED);

int cmp = key.compareTo(h.key);

if (cmp < 0) {

h.left = put(h.left, key, val);

} else if (cmp > 0) {

h.right = put(h.right, key, val);

} else {

h.val = val;

}

if (isRed(h.right) && !isRed(h.left)) h = rotateLeft(h);// lean left

if (isRed(h.left) && isRed(h.left.left)) h = rotateRight(h);//balance 4-node

if (isRed(h.left) && isRed(h.right)) flipColors(h);//split 4-node

return h;

}

红黑树的高度 h <= 2 lg N,证明:

Every path from root to null link has same number of black links.

Never two red links in-a-row.

B-Trees(B树)

最后简单提一下B树,就是将2-3树一般化,将每个节点的key-link pairs增加到 M - 1

At least 2 key-link pairs at root.

At least M / 2 key-link pairs in other nodes.

External nodes contain client keys.

Internal nodes contain copies of keys to guide search.

97d9a7cba9bbaacdbdc124ef2d08d374.png

在B树中查找

Start at root.

Find interval for search key and take corresponding link.

Search terminates in external node.

在B树中插入

Search for new key.

Insert at bottom.

Split nodes with M key-link pairs on the way up the tree.

命题:A search or an insertion in a B-tree of order M with N keys requires between log M-1 N and log M/2 N probes

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值