`
ordinary
  • 浏览: 79479 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

计算机程序设计艺术-----红黑树

 
阅读更多

自己写的红黑树,先保存下来,有时间写具体细节。

节点类:

package com.star;

 

public class RedBlack {

private RedBlack parent;

 

private RedBlack left;

 

private RedBlack right;

 

private RedBlackEnum redOrBlack;

private Integer value;

 

public Integer getValue() {

return value;

}

 

public void setValue(Integer value) {

this.value = value;

}

 

public RedBlack getParent() {

return parent;

}

 

public void setParent(RedBlack parent) {

this.parent = parent;

}

 

public RedBlack getLeft() {

return left;

}

 

public void setLeft(RedBlack left) {

this.left = left;

}

 

public RedBlack getRight() {

return right;

}

 

public void setRight(RedBlack right) {

this.right = right;

}

 

public RedBlackEnum getRedOrBlack() {

return redOrBlack;

}

 

public void setRedOrBlack(RedBlackEnum redOrBlack) {

this.redOrBlack = redOrBlack;

}

 

}

红黑枚举:
package com.star;

public enum RedBlackEnum {
red,
black;

}

红黑树:
package com.star;

import java.util.Scanner;

/**
 * 红黑树性质如下: 性质1. 节点是红色或黑色。 性质2. 根是黑色。 性质3. 所有叶子都是黑色(叶子是NIL节点)。 性质4.
 * 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点) 性质5.
 * 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
 * 
 * @author Administrator
 * 
 * @param <v>
 */
public class RedBlackTree {

private RedBlack root;

public RedBlackTree() {

}

public RedBlack getRoot() {
return root;
}

public void setRoot(RedBlack root) {
this.root = root;
}

public void insert(int value) {
RedBlack redBlack = insertTree(value);
adjustInsert(redBlack);
}

private RedBlack insertTree(int value) {
RedBlack redBlack = new RedBlack();
redBlack.setValue(value);
redBlack.setRedOrBlack(RedBlackEnum.red);
RedBlack redBlackIndex = root;
while (redBlackIndex != null && redBlackIndex.getValue() != null) {
if (redBlackIndex.getValue().compareTo(redBlack.getValue()) > 0) {
if (redBlackIndex.getLeft() == null) {
redBlackIndex.setLeft(redBlack);
redBlack.setParent(redBlackIndex);
break;
}
redBlackIndex = redBlackIndex.getLeft();
} else {
if (redBlackIndex.getRight() == null) {
redBlackIndex.setRight(redBlack);
redBlack.setParent(redBlackIndex);
break;
}
redBlackIndex = redBlackIndex.getRight();
}
}
return redBlack;
}

private RedBlack getGrandParent(RedBlack redBlack) {
if (redBlack != null && redBlack.getParent() != null) {
return redBlack.getParent().getParent();
}
return null;
}

private RedBlack getUncle(RedBlack redBlack) {
if (redBlack != null) {
if (redBlack.getParent().equals(getGrandParent(redBlack).getLeft())) {
return getGrandParent(redBlack).getRight();
} else {
return getGrandParent(redBlack).getLeft();
}
}
return null;
}

/**
* 情形1:
* 新节点N位于树的根上,没有父节点。在这种情形下,我们把它重绘为黑色以满足性质2[5]。因为它在每个路径上对黑节点数目增加一,性质5[4]符合。
* @param redBlack
*/
private void adjustInsert(RedBlack redBlack) {
if (redBlack != null) {
if (redBlack.getParent() == null) {
redBlack.setRedOrBlack(RedBlackEnum.black);
root = redBlack;
} else {
adjustInsertParentBlack(redBlack);
}
}

}

/**
* 2 新节点的父节点P是黑色,所以性质4[3]没有失效(新节点是红色的)。在这种情形下,树仍是有效的。性质5[4]受到威胁,
* 因为新节点N有两个黑色叶子儿子
* ;但是由于新节点N是红色,通过它的每个子节点的路径就都有同通过它所取代的黑色的叶子的路径同样数目的黑色节点,所以这个性质依然满足。
* @param redBlack
*/
private void adjustInsertParentBlack(RedBlack redBlack) {
if (redBlack.getParent().getRedOrBlack() == RedBlackEnum.black) {
return;
} else {
adjustInsertParentAndUncleRed(redBlack);
}

}

/**
* 如果父节点P和叔父节点U二者都是红色,(此时新插入节点N做为P的左子节点或右子节点都属于情形3,这里右图仅显示N做为P左子的情形)
* 则我们可以将它们两个重绘为黑色并重绘祖父节点G为红色
* (用来保持性质5[4])。现在我们的新节点N有了一个黑色的父节点P。因为通过父节点P或叔父节点U的任何路径都必定通过祖父节点G
* ,在这些路径上的黑节点数目没有改变
* 。但是,红色的祖父节点G的父节点也有可能是红色的,这就违反了性质4[3]。为了解决这个问题,我们在祖父节点G上递归地进行情形1的整个过程
* 。(把G当成是新加入的节点进行各种情况的检查)
* @param redBlack
*/
private void adjustInsertParentAndUncleRed(RedBlack redBlack) {
if (getUncle(redBlack) != null
&& getUncle(redBlack).getRedOrBlack() == RedBlackEnum.red) {
redBlack.getParent().setRedOrBlack(RedBlackEnum.black);
getUncle(redBlack).setRedOrBlack(RedBlackEnum.black);
getGrandParent(redBlack).setRedOrBlack(RedBlackEnum.red);
adjustInsert(getGrandParent(redBlack));
} else {
adjustInsertParentRedAndUncleBlack(redBlack);
}
}

/**
* 父节点P是红色而叔父节点U是黑色或缺少;
* 还有,新节点N是其父节点P的右子节点,而父节点P又是其父节点的左子节点。在这种情形下,我们进行一次左旋转调换新节点和其父节点的角色;
* 接着,我们按情形5处理以前的父节点P
* 。这导致某些路径通过它们以前不通过的新节点N或父节点P中的一个,但是这两个节点都是红色的,所以性质5[4]没有失效。
* @param redBlack
*/
private void adjustInsertParentRedAndUncleBlack(RedBlack redBlack) {
if (redBlack == redBlack.getParent().getRight()
&& redBlack.getParent() == getGrandParent(redBlack).getLeft()) {
leftRotate(redBlack.getParent());
redBlack = redBlack.getLeft();
} else if (redBlack == redBlack.getParent().getLeft()
&& redBlack.getParent() == getGrandParent(redBlack).getRight()) {
rightRotate(redBlack.getParent());
redBlack = redBlack.getRight();
}
adjustInsertParentRedAndUncleBlackOther(redBlack);
}

/**
* 父节点P是红色而叔父节点U 是黑色或缺少,新节点N 是其父节点的左子节点,而父节点P又是其父节点G的左子节点。在这种情形下,我们进行针对祖父节点G
* 的一次右旋转; 在旋转产生的树中,以前的父节点P现在是新节点N和以前的祖父节点G
* 的父节点。我们知道以前的祖父节点G是黑色,否则父节点P就不可能是红色 (如果 P 和 G 都是紅色就違反了性質4,所以 G
* 必須是黑色)。我们切换以前的父节点P和祖父节点G的颜色
* ,结果的树满足性质4[3]。性质5[4]也仍然保持满足,因为通过这三个节点中任何一个的所有路径以前都通过祖父节点G
* ,现在它们都通过以前的父节点P。在各自的情形下,这都是三个节点中唯一的黑色节点。
* @param redBlack
*/
private void adjustInsertParentRedAndUncleBlackOther(RedBlack redBlack) {
redBlack.getParent().setRedOrBlack(RedBlackEnum.black);
getGrandParent(redBlack).setRedOrBlack(RedBlackEnum.red);
if (redBlack == redBlack.getParent().getLeft()
&& redBlack.getParent() == getGrandParent(redBlack).getLeft()) {
rightRotate(getGrandParent(redBlack));
} else {
/* Here, n == n->parent->right && n->parent == grandparent(n)->right */
leftRotate(getGrandParent(redBlack));
}
}

private RedBlack getSibling(RedBlack redBlack) {
if (redBlack == redBlack.getParent().getLeft()) {
return redBlack.getParent().getRight();
} else {
return redBlack.getParent().getLeft();
}
}

private RedBlack find(int value) {
return find(root, value);
}

private RedBlack find(RedBlack parentRedBlack, int value) {
if (parentRedBlack == null) {
return null;
}
if (parentRedBlack.getValue() > value) {
return find(parentRedBlack.getLeft(), value);
} else if (parentRedBlack.getValue() < value) {
return find(parentRedBlack.getRight(), value);
} else {
return parentRedBlack;
}
}

private boolean isLeaf(RedBlack redBlack) {
if (redBlack == null) {
return true;
} else {
return false;
}
}

private void replace(RedBlack redBlack, RedBlack newRedBlack) {
newRedBlack.setLeft(redBlack.getLeft());
newRedBlack.setRight(redBlack.getRight());
newRedBlack.setParent(redBlack.getParent());
}

public void delete(int value) {
RedBlack redBlack = find(value);
RedBlack childRedBlack = isLeaf(redBlack.getRight()) ? redBlack
.getLeft() : redBlack.getRight();
replace(redBlack, childRedBlack);
if (redBlack.getRedOrBlack() == RedBlackEnum.black) {
if (childRedBlack.getRedOrBlack() == RedBlackEnum.red) {
childRedBlack.setRedOrBlack(RedBlackEnum.black);
} else {
delete_case1(childRedBlack);
}
}
redBlack = null;
}

/**
* 情况 1: N 是新的根。在这种情况下,我们就做完了。我们从所有路径去除了一个黑色节点,而新根是黑色的,所以属性都保持着。
* @param childRedBlack
*/
private void delete_case1(RedBlack redBlack) {
if (redBlack.getParent() != null) {
delete_case2(redBlack);
}

}

/**
* 情况 2: S 是红色。在这种情况下我们在N的父亲上做左旋转,把红色兄弟转换成N的祖父。我们接着对调 N
* 的父亲和祖父的颜色。尽管所有的路径仍然有相同数目的黑色节点,现在 N 有了一个黑色的兄弟和一个红色的父亲,所以我们可以接下去按
* 4、5或6情况来处理。(它的新兄弟是黑色因为它是红色S的一个儿子。)
* @param redBlack
*/
private void delete_case2(RedBlack redBlack) {
RedBlack sibling = getSibling(redBlack);
if (sibling.getRedOrBlack() == RedBlackEnum.red) {
redBlack.getParent().setRedOrBlack(RedBlackEnum.red);
sibling.setRedOrBlack(RedBlackEnum.black);
if (redBlack == redBlack.getParent().getLeft()) {
leftRotate(redBlack.getParent());
} else {
rightRotate(redBlack.getParent());
}
}
delete_case3(redBlack);

}

/**
* 情况 3: N 的父亲、S 和 S 的儿子都是黑色的。在这种情况下,我们简单的重绘 S 为红色。结果是通过S的所有路径,它们就是以前不通过 N
* 的那些路径,都少了一个黑色节点。因为删除 N 的初始的父亲使通过 N 的所有路径少了一个黑色节点,这使事情都平衡了起来。但是,通过 P
* 的所有路径现在比不通过 P 的路径少了一个黑色节点,所以仍然违反属性4。要修正这个问题,我们要从情况 1 开始,在 P 上做重新平衡处理。
* @param redBlack
*/
private void delete_case3(RedBlack redBlack) {
RedBlack sibling = getSibling(redBlack);
if (redBlack.getParent().getRedOrBlack() == RedBlackEnum.black
&& sibling.getRedOrBlack() == RedBlackEnum.black
&& sibling.getLeft().getRedOrBlack() == RedBlackEnum.black
&& sibling.getRight().getRedOrBlack() == RedBlackEnum.black) {
sibling.setRedOrBlack(RedBlackEnum.red);
delete_case1(redBlack.getParent());

} else {
delete_case4(redBlack);
}

}

/**
* S 和 S 的儿子都是黑色,但是 N 的父亲是红色。在这种情况下,我们简单的交换 N 的兄弟和父亲的颜色。这不影响不通过 N
* 的路径的黑色节点的数目,但是它在通过 N 的路径上对黑色节点数目增加了一,添补了在这些路径上删除的黑色节点。
* @param redBlack
*/
private void delete_case4(RedBlack redBlack) {
RedBlack sibling = getSibling(redBlack);
if (redBlack.getParent().getRedOrBlack() == RedBlackEnum.red
&& sibling.getRedOrBlack() == RedBlackEnum.black
&& sibling.getLeft().getRedOrBlack() == RedBlackEnum.black
&& sibling.getRight().getRedOrBlack() == RedBlackEnum.black) {
sibling.setRedOrBlack(RedBlackEnum.red);
redBlack.getParent().setRedOrBlack(RedBlackEnum.black);
} else {
delete_case5(redBlack);
}
}

/**
* 情况 5: S 是黑色,S 的左儿子是红色,S 的右儿子是黑色,而 N 是它父亲的左儿子。在这种情况下我们在 S 上做右旋转,这样 S
* 的左儿子成为 S 的父亲和 N 的新兄弟。我们接着交换 S 和它的新父亲的颜色。所有路径仍有同样数目的黑色节点,但是现在 N
* 有了一个右儿子是红色的黑色兄弟,所以我们进入了情况 6。N 和它的父亲都不受这个变换的影响。
* @param redBlack
*/
private void delete_case5(RedBlack redBlack) {
RedBlack sibling = getSibling(redBlack);
if (sibling.getRedOrBlack() == RedBlackEnum.black) {
if (redBlack == redBlack.getParent().getLeft()
&& sibling.getRight().getRedOrBlack() == RedBlackEnum.black
&& sibling.getLeft().getRedOrBlack() == RedBlackEnum.red) {
sibling.setRedOrBlack(RedBlackEnum.red);
sibling.getLeft().setRedOrBlack(RedBlackEnum.black);
rightRotate(sibling);
} else if (redBlack == redBlack.getParent().getRight()
&& sibling.getLeft().getRedOrBlack() == RedBlackEnum.black
&& sibling.getRight().getRedOrBlack() == RedBlackEnum.red) {
sibling.setRedOrBlack(RedBlackEnum.red);
sibling.getRight().setRedOrBlack(RedBlackEnum.black);
leftRotate(sibling);
}
}
detete_case6(redBlack);
}

/**
* 情况 6: S 是黑色,S 的右儿子是红色,而 N 是它父亲的左儿子。在这种情况下我们在 N 的父亲上做左旋转,这样 S 成为 N 的父亲和 S
* 的右儿子的父亲。我们接着交换 N 的父亲和 S 的颜色,并使 S 的右儿子为黑色。子树在它的根上的仍是同样的颜色,所以属性 3
* 没有被违反。但是,N 现在增加了一个黑色祖先: 要么 N 的父亲变成黑色,要么它是黑色而 S 被增加为一个黑色祖父。所以,通过 N
* 的路径都增加了一个黑色节点。 此时,如果一个路径不通过 N,则有两种可能性: 它通过 N 的新兄弟。那么它以前和现在都必定通过 S 和 N
* 的父亲,而它们只是交换了颜色。所以路径保持了同样数目的黑色节点。 它通过 N 的新叔父,S 的右儿子。那么它以前通过 S、S 的父亲和 S
* 的右儿子,但是现在只通过 S,它被假定为它以前的父亲的颜色,和 S 的右儿子,它被从红色改变为黑色。合成效果是这个路径通过了同样数目的黑色节点。
* @param redBlack
*/
private void detete_case6(RedBlack redBlack) {
RedBlack sibling = getSibling(redBlack);
sibling.setRedOrBlack(redBlack.getParent().getRedOrBlack());
redBlack.getParent().setRedOrBlack(RedBlackEnum.black);
if (redBlack == redBlack.getParent().getLeft()) {
sibling.getRight().setRedOrBlack(RedBlackEnum.black);
leftRotate(redBlack.getParent());
} else {
sibling.getLeft().setRedOrBlack(RedBlackEnum.black);
rightRotate(redBlack.getParent());
}
}

public void print() {
print(root);
}

private void print(RedBlack redBlack) {
if (redBlack.getLeft() != null) {
print(redBlack.getLeft());
}
System.out.print(redBlack.getValue() + " ");
if (redBlack.getRight() != null) {
print(redBlack.getRight());
}
}

private void leftRotate(RedBlack redBlack) {
if (redBlack != null) {
RedBlack right = redBlack.getRight();
if (right != null) {
redBlack.setRight(right.getLeft());
if (right.getLeft() != null) {
right.getLeft().setParent(redBlack);
}
right.setLeft(redBlack);
right.setParent(redBlack.getParent());
if (right.getParent() == null) {
root = right;
}
redBlack.setParent(right);
if (right.getParent() != null) {
if (right.getParent().getLeft().getValue().equals(
redBlack.getValue())) {
right.getParent().setLeft(right);
} else {
right.getParent().setRight(right);
}
}

}
}
}

private void rightRotate(RedBlack redBlack) {
if (redBlack != null) {
RedBlack left = redBlack.getLeft();
if (left != null) {
redBlack.setLeft(left.getRight());
if (left.getRight() != null) {
left.getRight().setParent(redBlack);
}
left.setRight(redBlack);
left.setParent(redBlack.getParent());
if (left.getParent() == null) {
root = left;
}
if (left.getParent() != null) {
if (left.getParent().getLeft().getValue().equals(
redBlack.getValue())) {
left.getParent().setLeft(left);
} else {
left.getParent().setRight(left);
}
}
}
}

}

public static void main(String[] args) {
RedBlackTree tree = new RedBlackTree();
Scanner s = new Scanner(System.in);
int a = s.nextInt();
while (a != 0) {
tree.insert(a);
a = s.nextInt();
}
tree.print();
}
}

分享到:
评论

相关推荐

    计算机程序设计艺术(第二卷).pdf

    根据提供的文件信息,“计算机程序设计艺术(第二卷).pdf”,我们可以推断出这份文档主要涉及计算机程序设计领域的深入探讨和技术细节。然而,由于提供的内容非常有限,我们将基于标题、描述以及部分可见的信息来...

    计算机程序设计艺术1-3卷.zip

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由美国计算机科学家Donald E. Knuth撰写。这套书籍涵盖了程序设计、算法分析以及计算理论等多个方面的深度内容,对程序员、软件工程师以及计算机科学学者...

    计算机程序设计艺术PDF

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由美国计算机科学家Donald Knuth撰写。这套书共计三卷,深入探讨了算法和程序设计的精髓,为读者提供了丰富的理论基础和实践经验。每一卷都包含了大量精心...

    计算机程序设计艺术,扫描版,全三卷

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由著名计算机科学家Donald Knuth撰写。这套书系统地探讨了程序设计的理论和实践,涵盖了从基础算法到复杂数据结构的广泛主题,对于深入理解计算机科学的核心...

    计算机程序设计艺术 第 3 卷 (中文版)

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由Donald E. Knuth撰写。这部巨著分为多卷,每一卷深入探讨了程序设计的各个方面。本问题中提到的是第3卷,虽然描述中提到了第2卷,但我们的焦点在于第3卷的...

    《计算机程序设计艺术(第3卷)》 第3卷:排序与查找 (第二版) 高清中文版 PDF

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由著名计算机科学家Donald Knuth撰写。这本书的第3卷专门探讨了排序与查找这两个核心的算法主题,它们是编程和数据处理的基础。在本卷中,Knuth深入剖析了...

    计算机程序设计艺术全三卷

    《计算机程序设计艺术》全三卷是一套由计算机科学巨擘Donald E. Knuth编著的经典著作,中文版包括三个PDF文档,全面深入地探讨了程序设计的精髓和技巧。这套书籍是程序员、计算机科学家以及对算法和软件工程感兴趣...

    计算机程序设计艺术+第3卷:排序与查找(第二版)高清中文版

    《计算机程序设计艺术》是由美国计算机科学家Donald Knuth所著的一套经典计算机科学丛书,它在IT领域享有极高的声誉,被众多专业人士视为必读之作,包括比尔·盖茨在内的许多技术大牛都曾给予高度评价。这套书籍深入...

    计算机程序设计艺术(第3卷)

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由著名计算机科学家Donald Knuth撰写。这部系列作品全面深入地探讨了程序设计的各个方面,旨在提高程序员的技艺和软件的质量。第三卷,通常称为“排序与搜索...

    计算机程序设计艺术(第1+2卷):

    根据提供的文件信息,我们可以推断出这是一份关于《计算机程序设计艺术(第1+2卷)》资源的分享文档,主要涉及程序设计与算法领域。下面将对这份资料所包含的重要知识点进行详细阐述。 ### 计算机程序设计艺术 ###...

    计算机程序设计艺术第四部分(共四部分)

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由世界著名计算机科学家Donald E. Knuth撰写。这部作品共分为四部分,包含了七卷,深入探讨了算法的设计、分析和实现,对于理解和提升编程技能有着深远的...

    计算机程序设计艺术(一、二、三卷).rar

    《计算机程序设计艺术》是一套由计算机科学先驱Donald E. Knuth编写的经典著作,共分为七卷,其中我们拥有的是第一、第二和第三卷。这套书深入探讨了算法设计与分析的基础,以及编程语言的设计原理。以下是每卷的...

    计算机程序设计艺术(卷一)

    《计算机程序设计艺术》是计算机科学领域的一部经典之作,由世界知名计算机科学家Donald E. Knuth撰写。这本书被广泛认为是20世纪最杰出的20本著作之一,其影响力与爱因斯坦的《相对论》相提并论,充分体现了在...

    计算机程序设计艺术 第三版第一卷:基本算法

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由美国计算机科学家Donald E. Knuth撰写。这部巨著共分为七卷,其中第一卷名为“基本算法”,它深入探讨了编程的艺术,提供了广泛而深入的算法分析和实现。...

    计算机程序设计艺术(卷2)

    《计算机程序设计艺术》第二卷详细讲解了二叉树、平衡树、B树等树的结构和树上的算法,比如二叉搜索树的实现,以及AVL树和红黑树等平衡二叉树的应用。 3. 优先队列:优先队列是一种数据结构,其中每个元素都有一个...

    计算机程序设计艺术_第1卷_基本算法

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由美国计算机科学家Donald E. Knuth撰写。这部巨著共分为七卷,其中第一卷名为《基本算法》。本卷主要探讨了计算机科学中的基础算法理论和技术,为后续的...

    计算机程序设计艺术第三卷 排序与查找

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由著名计算机科学家Donald Knuth撰写。这本书分为多卷,每卷深入探讨了编程和算法设计的各个方面。第三卷专门聚焦于“排序与查找”,这是所有程序员和计算机...

    计算机程序设计艺术(第三卷)

    《计算机程序设计艺术》是由高德纳(Donald E. Knuth)编写的系列书籍,被称为计算机科学领域的经典之作。该系列共分为四卷,但其中第三卷在撰写时被分割为两部分,即第三卷A和第三卷B。本书主要围绕数据结构、排序...

    计算机程序设计艺术第三版一卷 算法基础

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由著名计算机科学家Donald E. Knuth撰写。第三版的第一卷,专门探讨了“算法基础”,是深入理解和掌握算法设计与分析的重要参考资料。这本书旨在为程序员、...

    计算机程序设计艺术(第3卷)(第2版).rar

    《计算机程序设计艺术》是计算机科学领域的一部经典著作,由著名计算机科学家Donald Knuth撰写。这本书分为多卷,深入探讨了程序设计的各种方法和技术,尤其是算法的设计与分析。第3卷聚焦于排序和搜索算法,是算法...

Global site tag (gtag.js) - Google Analytics