0 课程地址
https://coding.imooc.com/lesson/207.html#mid=15185
1 重点关注
1.1 红黑树本节解析草图
- 2节点添加节点:
如下图(其实有4种情况):
A 黑Node 左侧 添加 红Node, 正常
B 黑Node 右侧 添加 红Node, 右节点 为红,左节点为黑, 左旋转,形成A的情况
C 红Node 右侧 添加 红Node, 左旋转, 形成红Node 左侧挂红Node的情况,这个在下图2.B可以解决
D 红Node 左侧 添加 红Node, 如下图1.C情况,在2.B可以解决
3节点添加节点
A 父Node 右侧 添加 红Node, 形成了4节点,颜色翻转,4节点拆分,父节点向上融合
B 子Node 左侧 添加 红Node, 父节点右旋转,再颜色翻转,4节点拆分,父节点向上融合
C 子Node 右侧 添加 红Node, 子节点左旋转,父节点右旋转,再颜色翻转,4节点拆分,父节点向上融合
关于为什么4节点不会出现父节点是红色的情况,首先,如果父节点为根节点,只能是黑。往下推,只有2中的三种情况,结果都是子节点为黑,父节点为红(如果父节点为根节点4置黑了)
2 课程内容
3 Coding
3.1 红黑树Coding
关键代码:
if(isRed(node.right)&&!isRed(node.left)){ node = leftRotate(node); } if(isRed(node.left)&&isRed(node.left.left)){ node = rightRotate(node); } if(isRed(node.left)&&isRed(node.right)){ flipColors(node); }
红黑树类:
package com.company; import java.util.ArrayList; import java.util.List; public class RBTree<K extends Comparable<K>,V> { private static final boolean RED = true; private static final boolean BLACK = false; //1 定义Node class Node{ private K key; private V value; private Node left,right; private int height; private boolean color; public Node(K key, V value){ this.key = key; this.value = value; this.left = null; this.right = null; this.height = 1; this.color = RED; } @Override public String toString() { final StringBuffer sb = new StringBuffer("Node{"); sb.append("key=").append(key); sb.append(", value=").append(value); sb.append('}'); return sb.toString(); } } //2 定义属性 private int size; private Node root; /** * 获取节点颜色 * @author weidoudou * @date 2023/4/21 20:35 * @param node 请添加参数描述 * @return boolean **/ private boolean isRed(Node node){ if(node == null){ return BLACK; } return node.color; } /** * 无参构造函数 * @author weidoudou * @date 2023/1/1 11:09 * @return null **/ public RBTree(){ this.size = 0; this.root = null; } public boolean isEmpty() { return size==0?true:false; } public int getSize() { return size; } //3 定义包含函数 private Node containsKey(K key,Node node){ //结束条件 if(null==node){ return null; } //循环条件 if(key.compareTo(node.key)<0){ return containsKey(key,node.left); }else if(key.compareTo(node.key)>0){ return containsKey(key, node.right); }else{//key.compareTo(node.key)=0 其实这个也是结束条件 return node; } } public boolean contains(K key) { return containsKey(key,root)==null?false:true; } public V get(K key) { Node node = containsKey(key,root); if(null!=node){ return node.value; } return null; } //3 递归,添加元素 public Node add(K key,V value,Node node){ if(node == null){ size ++; return new Node(key, value); // 默认插入红色节点 } if(key.compareTo(node.key) < 0) node.left = add(key, value,node.left); else if(key.compareTo(node.key) > 0) node.right = add(key, value,node.right); else // key.compareTo(node.key) == 0 node.value = value; if(isRed(node.right)&&!isRed(node.left)){ node = leftRotate(node); } if(isRed(node.left)&&isRed(node.left.left)){ node = rightRotate(node); } if(isRed(node.left)&&isRed(node.right)){ flipColors(node); } return node; } /** * 颜色翻转 * @author weidoudou * @date 2023/5/6 6:37 * @param node 请添加参数描述 * @return void **/ private void flipColors(Node node){ node.left.color = BLACK; node.right.color = BLACK; node.color = RED; } // node x // / \ 左旋转 / \ // T1 x ---------> node T3 // / \ / \ // T2 T3 T1 T2 private Node leftRotate(Node node) { Node x = node.right; Node T2 = x.left; // 向左旋转过程 node.right = T2; x.left = node; // 更新color x.color = node.color; node.color = RED; return x; } // node x // / \ 右旋转 / \ // x T2 -------> y node // / \ / \ // y T1 T1 T2 private Node rightRotate(Node node){ Node x = node.left; node.left = x.right; x.right = node; x.color = node.color; node.color = RED; return x; } /** * 添加节点后根节点设置为黑色 * @author weidoudou * @date 2023/5/5 7:01 * @param key 请添加参数描述 * @param value 请添加参数描述 * @return void **/ public void add(K key, V value) { root = add(key,value,root); root.color = BLACK; } public void set(K key, V value) { Node node = containsKey(key,root); if(node == null){ throw new IllegalArgumentException("要修改的值不存在"); } node.value = value; } private Node remove(Node node, K key){ if( node == null ) return null; if( key.compareTo(node.key) < 0 ){ node.left = remove(node.left , key); return node; } else if(key.compareTo(node.key) > 0 ){ node.right = remove(node.right, key); return node; } else{ // key.compareTo(node.key) == 0 // 待删除节点左子树为空的情况 if(node.left == null){ Node rightNode = node.right; node.right = null; size --; return rightNode; } // 待删除节点右子树为空的情况 if(node.right == null){ Node leftNode = node.left; node.left = null; size --; return leftNode; } // 待删除节点左右子树均不为空的情况 // 找到比待删除节点大的最小节点, 即待删除节点右子树的最小节点 // 用这个节点顶替待删除节点的位置 Node successor = findMin(node.right); successor.right = removMin(node.right); successor.left = node.left; node.left = node.right = null; return successor; } } private Node findMin(Node node){ //1 终止条件 if(node.left==null){ return node; } //2 递归 return findMin(node.left); } private Node removMin(Node node){ //终止条件 if(node.left==null){ Node rightNode = node.right; node.right = null; return rightNode; } //递归 node.left = removMin(node.left); return node; } /** * 删除任意元素 若删除元素节点下只有一个节点直接接上即可,若有两个节点,则找前驱或后继,本节找前驱 * @author weidoudou * @date 2023/1/1 11:52 * @param key 请添加参数描述 * @return V **/ public V remove(K key) { Node node = containsKey(key,root); if(node != null){ root = remove(root, key); return node.value; } return null; } //1 校验二分搜索树(中序遍历参考之前的中序遍历一节) public boolean judgeBST(){ List<K> list = new ArrayList<>(); inOrder(root,list); for(int i=1;i<list.size();i++){ if(list.get(i-1).compareTo(list.get(i))>0){ return false; } } return true; } private void inOrder(Node node, List<K> list){ if(node==null){ return; } inOrder(node.left,list); list.add(node.key); inOrder(node.right,list); } public static void main(String[] args){ System.out.println("Pride and Prejudice"); ArrayList<String> words = new ArrayList<>(); if(FileOperation.readFile("pride-and-prejudice.txt", words)) { System.out.println("Total words: " + words.size()); RBTree<String, Integer> map = new RBTree<>(); for (String word : words) { if (map.contains(word)) map.set(word, map.get(word) + 1); else map.add(word, 1); } System.out.println("Total different words: " + map.getSize()); System.out.println("Frequency of PRIDE: " + map.get("pride")); System.out.println("Frequency of PREJUDICE: " + map.get("prejudice")); } System.out.println(); } }
测试结果:
Pride and Prejudice Total words: 125901 Total different words: 6530 Frequency of PRIDE: 53 Frequency of PREJUDICE: 11 Process finished with exit code 0
诸葛