forked from sduonline/sc-resources
增加淘宝群内容,修改部分文件组织
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
17
数据结构(双语)/数据结构 day16【瑞客论坛 www.ruike1.com】/代码/11-哈希表/.project
Normal file
17
数据结构(双语)/数据结构 day16【瑞客论坛 www.ruike1.com】/代码/11-哈希表/.project
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>11-哈希表</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
@@ -0,0 +1,11 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,11 @@
|
||||
package com.mj;
|
||||
|
||||
public class Asserts {
|
||||
public static void test(boolean value) {
|
||||
try {
|
||||
if (!value) throw new Exception("测试未通过");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,259 @@
|
||||
package com.mj;
|
||||
|
||||
import com.mj.Times.Task;
|
||||
import com.mj.file.FileInfo;
|
||||
import com.mj.file.Files;
|
||||
import com.mj.map.HashMap;
|
||||
import com.mj.map.Map;
|
||||
import com.mj.map.Map.Visitor;
|
||||
import com.mj.model.Key;
|
||||
import com.mj.model.Person;
|
||||
import com.mj.model.SubKey1;
|
||||
import com.mj.model.SubKey2;
|
||||
|
||||
public class Main {
|
||||
|
||||
// static void test1() {
|
||||
// String string = "jack"; // 3254239
|
||||
// System.out.println(string.hashCode());
|
||||
//// int len = string.length();
|
||||
//// int hashCode = 0;
|
||||
//// for (int i = 0; i < len; i++) {
|
||||
//// char c = string.charAt(i);
|
||||
//// hashCode = hashCode * 31 + c;
|
||||
//// // hashCode = (hashCode << 5) - hashCode + c;
|
||||
//// }
|
||||
//// System.out.println(hashCode);
|
||||
// // hashCode = ((j * 31 + a) * 31 + c) * 31 + k
|
||||
// }
|
||||
//
|
||||
// static void test2() {
|
||||
// Integer a = 110;
|
||||
// Float b = 10.6f;
|
||||
// Long c = 156l;
|
||||
// Double d = 10.9;
|
||||
// String e = "rose";
|
||||
//
|
||||
// System.out.println(a.hashCode());
|
||||
// System.out.println(b.hashCode());
|
||||
// System.out.println(c.hashCode());
|
||||
// System.out.println(d.hashCode());
|
||||
// System.out.println(e.hashCode());
|
||||
// }
|
||||
//
|
||||
// static void test3() {
|
||||
// Person p1 = new Person(10, 1.67f, "jack");
|
||||
// Person p2 = new Person(10, 1.67f, "jack");
|
||||
//
|
||||
// Map<Object, Object> map = new HashMap<>();
|
||||
// map.put(p1, "abc");
|
||||
// map.put("test", "ccc");
|
||||
// map.put(p2, "bcd");
|
||||
// System.out.println(map.size());
|
||||
// }
|
||||
//
|
||||
// static void test4() {
|
||||
// Person p1 = new Person(10, 1.67f, "jack");
|
||||
// Person p2 = new Person(10, 1.67f, "jack");
|
||||
//
|
||||
// Map<Object, Integer> map = new HashMap<>();
|
||||
// map.put(p1, 1);
|
||||
// map.put(p2, 2);
|
||||
// map.put("jack", 3);
|
||||
// map.put("rose", 4);
|
||||
// map.put("jack", 5);
|
||||
// map.put(null, 6);
|
||||
//
|
||||
//// System.out.println(map.size());
|
||||
//// System.out.println(map.remove("jack"));
|
||||
//// System.out.println(map.get("jack"));
|
||||
//// System.out.println(map.size());
|
||||
//
|
||||
// System.out.println(map.containsKey(p1));
|
||||
// System.out.println(map.containsKey(null));
|
||||
// System.out.println(map.containsValue(6));
|
||||
// System.out.println(map.containsValue(1));
|
||||
//
|
||||
//// map.traversal(new Visitor<Object, Integer>() {
|
||||
//// public boolean visit(Object key, Integer value) {
|
||||
//// System.out.println(key + "_" + value);
|
||||
//// return false;
|
||||
//// }
|
||||
//// });
|
||||
//
|
||||
//// System.out.println(map.get("jack"));
|
||||
//// System.out.println(map.get("rose"));
|
||||
//// System.out.println(map.get(null));
|
||||
//// System.out.println(map.get(p1));
|
||||
// }
|
||||
//
|
||||
// static void test5() {
|
||||
// Person p1 = new Person(10, 1.67f, "jack");
|
||||
// Person p2 = new Person(10, 1.67f, "jack");
|
||||
//
|
||||
// Map<Object, Integer> map = new HashMap<>();
|
||||
// map.put(p1, 1);
|
||||
// map.put(p2, 2);
|
||||
// map.put("jack", 3);
|
||||
// map.put("rose", 4);
|
||||
// map.put("jack", 5);
|
||||
// map.put(null, 6);
|
||||
//
|
||||
//// System.out.println(map.size());
|
||||
//// System.out.println(map.remove("jack"));
|
||||
//// System.out.println(map.get("jack"));
|
||||
//// System.out.println(map.size());
|
||||
//
|
||||
// System.out.println(map.containsKey(p1));
|
||||
// System.out.println(map.containsKey(null));
|
||||
// System.out.println(map.containsValue(6));
|
||||
// System.out.println(map.containsValue(1));
|
||||
//
|
||||
//// map.traversal(new Visitor<Object, Integer>() {
|
||||
//// public boolean visit(Object key, Integer value) {
|
||||
//// System.out.println(key + "_" + value);
|
||||
//// return false;
|
||||
//// }
|
||||
//// });
|
||||
//
|
||||
//// System.out.println(map.get("jack"));
|
||||
//// System.out.println(map.get("rose"));
|
||||
//// System.out.println(map.get(null));
|
||||
//// System.out.println(map.get(p1));
|
||||
// }
|
||||
//
|
||||
// static void test6() {
|
||||
// HashMap<Object, Integer> map = new HashMap<>();
|
||||
// for (int i = 1; i <= 19; i++) {
|
||||
// map.put(new Key(i), i);
|
||||
// }
|
||||
//
|
||||
// map.put(new Key(4), 100);
|
||||
// Asserts.test(map.size() == 19);
|
||||
// Asserts.test(map.get(new Key(4)) == 100);
|
||||
// Asserts.test(map.get(new Key(18)) == 18);
|
||||
// }
|
||||
//
|
||||
|
||||
static void test2(HashMap<Object, Integer> map) {
|
||||
for (int i = 1; i <= 20; i++) {
|
||||
map.put(new Key(i), i);
|
||||
}
|
||||
for (int i = 5; i <= 7; i++) {
|
||||
map.put(new Key(i), i + 5);
|
||||
}
|
||||
Asserts.test(map.size() == 20);
|
||||
Asserts.test(map.get(new Key(4)) == 4);
|
||||
Asserts.test(map.get(new Key(5)) == 10);
|
||||
Asserts.test(map.get(new Key(6)) == 11);
|
||||
Asserts.test(map.get(new Key(7)) == 12);
|
||||
Asserts.test(map.get(new Key(8)) == 8);
|
||||
}
|
||||
|
||||
static void test3(HashMap<Object, Integer> map) {
|
||||
map.put(null, 1); // 1
|
||||
map.put(new Object(), 2); // 2
|
||||
map.put("jack", 3); // 3
|
||||
map.put(10, 4); // 4
|
||||
map.put(new Object(), 5); // 5
|
||||
map.put("jack", 6);
|
||||
map.put(10, 7);
|
||||
map.put(null, 8);
|
||||
map.put(10, null);
|
||||
Asserts.test(map.size() == 5);
|
||||
Asserts.test(map.get(null) == 8);
|
||||
Asserts.test(map.get("jack") == 6);
|
||||
Asserts.test(map.get(10) == null);
|
||||
Asserts.test(map.get(new Object()) == null);
|
||||
Asserts.test(map.containsKey(10));
|
||||
Asserts.test(map.containsKey(null));
|
||||
Asserts.test(map.containsValue(null));
|
||||
Asserts.test(map.containsValue(1) == false);
|
||||
}
|
||||
|
||||
static void test4(HashMap<Object, Integer> map) {
|
||||
map.put("jack", 1);
|
||||
map.put("rose", 2);
|
||||
map.put("jim", 3);
|
||||
map.put("jake", 4);
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
map.put("test" + i, i);
|
||||
map.put(new Key(i), i);
|
||||
}
|
||||
for (int i = 5; i <= 7; i++) {
|
||||
Asserts.test(map.remove(new Key(i)) == i);
|
||||
}
|
||||
for (int i = 1; i <= 3; i++) {
|
||||
map.put(new Key(i), i + 5);
|
||||
}
|
||||
Asserts.test(map.size() == 21);
|
||||
Asserts.test(map.get(new Key(1)) == 6);
|
||||
Asserts.test(map.get(new Key(2)) == 7);
|
||||
Asserts.test(map.get(new Key(3)) == 8);
|
||||
Asserts.test(map.get(new Key(4)) == 4);
|
||||
Asserts.test(map.get(new Key(5)) == null);
|
||||
Asserts.test(map.get(new Key(6)) == null);
|
||||
Asserts.test(map.get(new Key(7)) == null);
|
||||
Asserts.test(map.get(new Key(8)) == 8);
|
||||
}
|
||||
|
||||
static void test5(HashMap<Object, Integer> map) {
|
||||
for (int i = 1; i <= 20; i++) {
|
||||
map.put(new SubKey1(i), i);
|
||||
}
|
||||
map.put(new SubKey2(1), 5);
|
||||
Asserts.test(map.get(new SubKey1(1)) == 5);
|
||||
Asserts.test(map.get(new SubKey2(1)) == 5);
|
||||
Asserts.test(map.size() == 20);
|
||||
}
|
||||
|
||||
static void test1() {
|
||||
String filepath = "C:\\Users\\MJ Lee\\Desktop\\src\\java\\util";
|
||||
FileInfo fileInfo = Files.read(filepath, null);
|
||||
String[] words = fileInfo.words();
|
||||
|
||||
System.out.println("总行数:" + fileInfo.getLines());
|
||||
System.out.println("单词总数:" + words.length);
|
||||
System.out.println("-------------------------------------");
|
||||
|
||||
// java.util.HashMap<String, Integer> map = new java.util.HashMap<>();
|
||||
//
|
||||
// for (String word : words) {
|
||||
// Integer count = map.get(word);
|
||||
// count = count == null ? 0 : count;
|
||||
// map.put(word, count + 1);
|
||||
// }
|
||||
// System.out.println(map.size()); // 17188
|
||||
|
||||
HashMap<String, Integer> map = new HashMap<>();
|
||||
Times.test(map.getClass().getName(), new Task() {
|
||||
@Override
|
||||
public void execute() {
|
||||
for (String word : words) {
|
||||
Integer count = map.get(word);
|
||||
count = count == null ? 0 : count;
|
||||
map.put(word, count + 1);
|
||||
}
|
||||
System.out.println(map.size()); // 17188
|
||||
|
||||
int count = 0;
|
||||
for (String word : words) {
|
||||
Integer i = map.get(word);
|
||||
count += i == null ? 0 : i;
|
||||
map.remove(word);
|
||||
}
|
||||
Asserts.test(count == words.length);
|
||||
Asserts.test(map.size() == 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
test1();
|
||||
test2(new HashMap<>());
|
||||
test3(new HashMap<>());
|
||||
test4(new HashMap<>());
|
||||
test5(new HashMap<>());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.mj;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
|
||||
public class Times {
|
||||
private static final SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss.SSS");
|
||||
|
||||
public interface Task {
|
||||
void execute();
|
||||
}
|
||||
|
||||
public static void test(String title, Task task) {
|
||||
if (task == null) return;
|
||||
title = (title == null) ? "" : ("【" + title + "】");
|
||||
System.out.println(title);
|
||||
System.out.println("开始:" + fmt.format(new Date()));
|
||||
long begin = System.currentTimeMillis();
|
||||
task.execute();
|
||||
long end = System.currentTimeMillis();
|
||||
System.out.println("结束:" + fmt.format(new Date()));
|
||||
double delta = (end - begin) / 1000.0;
|
||||
System.out.println("耗时:" + delta + "秒");
|
||||
System.out.println("-------------------------------------");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.mj.file;
|
||||
|
||||
public class FileInfo {
|
||||
private int lines;
|
||||
private int files;
|
||||
private String content = "";
|
||||
|
||||
public String[] words() {
|
||||
return content.split("[^a-zA-Z]+");
|
||||
}
|
||||
|
||||
public int getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
public void setFiles(int files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
public int getLines() {
|
||||
return lines;
|
||||
}
|
||||
|
||||
public void setLines(int lines) {
|
||||
this.lines = lines;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public FileInfo append(FileInfo info) {
|
||||
if (info != null && info.lines > 0) {
|
||||
this.files += info.files;
|
||||
this.lines += info.lines;
|
||||
this.content = new StringBuilder(this.content)
|
||||
.append("\n")
|
||||
.append(info.content)
|
||||
.toString();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.mj.file;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
|
||||
public class Files {
|
||||
|
||||
/**
|
||||
* 读取文件内容
|
||||
* @param file
|
||||
* @return
|
||||
*/
|
||||
public static FileInfo read(String file) {
|
||||
if (file == null) return null;
|
||||
FileInfo info = new FileInfo();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
try (FileReader reader = new FileReader(file);
|
||||
BufferedReader br = new BufferedReader(reader)) {
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
info.setLines(info.getLines() + 1);
|
||||
}
|
||||
int len = sb.length();
|
||||
if (len > 0) {
|
||||
sb.deleteCharAt(len - 1);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
info.setFiles(info.getFiles() + 1);
|
||||
info.setContent(sb.toString());
|
||||
return info;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取文件夹下面的文件内容
|
||||
* @param dir
|
||||
* @param extensions
|
||||
* @return
|
||||
*/
|
||||
public static FileInfo read(String dir, String[] extensions) {
|
||||
if (dir == null) return null;
|
||||
|
||||
File dirFile = new File(dir);
|
||||
if (!dirFile.exists()) return null;
|
||||
|
||||
FileInfo info = new FileInfo();
|
||||
dirFile.listFiles(new FileFilter() {
|
||||
public boolean accept(File subFile) {
|
||||
String subFilepath = subFile.getAbsolutePath();
|
||||
if (subFile.isDirectory()) {
|
||||
info.append(read(subFilepath, extensions));
|
||||
} else if (extensions != null && extensions.length > 0) {
|
||||
for (String extension : extensions) {
|
||||
if (subFilepath.endsWith("." + extension)) {
|
||||
info.append(read(subFilepath));
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
info.append(read(subFilepath));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return info;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,594 @@
|
||||
package com.mj.map;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
|
||||
import org.w3c.dom.ls.LSException;
|
||||
|
||||
import com.mj.printer.BinaryTreeInfo;
|
||||
import com.mj.printer.BinaryTrees;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public class HashMap<K, V> implements Map<K, V> {
|
||||
private static final boolean RED = false;
|
||||
private static final boolean BLACK = true;
|
||||
private int size;
|
||||
private Node<K, V>[] table;
|
||||
private static final int DEFAULT_CAPACITY = 1 << 4;
|
||||
|
||||
public HashMap() {
|
||||
table = new Node[DEFAULT_CAPACITY];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
if (size == 0) return;
|
||||
size = 0;
|
||||
for (int i = 0; i < table.length; i++) {
|
||||
table[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
int index = index(key);
|
||||
// 取出index位置的红黑树根节点
|
||||
Node<K, V> root = table[index];
|
||||
if (root == null) {
|
||||
root = new Node<>(key, value, null);
|
||||
table[index] = root;
|
||||
size++;
|
||||
afterPut(root);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 添加新的节点到红黑树上面
|
||||
Node<K, V> parent = root;
|
||||
Node<K, V> node = root;
|
||||
int cmp = 0;
|
||||
K k1 = key;
|
||||
int h1 = k1 == null ? 0 : k1.hashCode();
|
||||
Node<K, V> result = null;
|
||||
boolean searched = false; // 是否已经搜索过这个key
|
||||
do {
|
||||
parent = node;
|
||||
K k2 = node.key;
|
||||
int h2 = node.hash;
|
||||
if (h1 > h2) {
|
||||
cmp = 1;
|
||||
} else if (h1 < h2) {
|
||||
cmp = -1;
|
||||
} else if (Objects.equals(k1, k2)) {
|
||||
cmp = 0;
|
||||
} else if (k1 != null && k2 != null
|
||||
&& k1.getClass() == k2.getClass()
|
||||
&& k1 instanceof Comparable
|
||||
&& (cmp = ((Comparable) k1).compareTo(k2)) != 0) {
|
||||
|
||||
} else if (searched) { // 已经扫描了
|
||||
cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
|
||||
} else { // searched == false; 还没有扫描,然后再根据内存地址大小决定左右
|
||||
if ((node.left != null && (result = node(node.left, k1)) != null)
|
||||
|| (node.right != null && (result = node(node.right, k1)) != null)) {
|
||||
// 已经存在这个key
|
||||
node = result;
|
||||
cmp = 0;
|
||||
} else { // 不存在这个key
|
||||
searched = true;
|
||||
cmp = System.identityHashCode(k1) - System.identityHashCode(k2);
|
||||
}
|
||||
}
|
||||
|
||||
if (cmp > 0) {
|
||||
node = node.right;
|
||||
} else if (cmp < 0) {
|
||||
node = node.left;
|
||||
} else { // 相等
|
||||
V oldValue = node.value;
|
||||
node.key = key;
|
||||
node.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
} while (node != null);
|
||||
|
||||
// 看看插入到父节点的哪个位置
|
||||
Node<K, V> newNode = new Node<>(key, value, parent);
|
||||
if (cmp > 0) {
|
||||
parent.right = newNode;
|
||||
} else {
|
||||
parent.left = newNode;
|
||||
}
|
||||
size++;
|
||||
|
||||
// 新添加节点之后的处理
|
||||
afterPut(newNode);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
Node<K, V> node = node(key);
|
||||
return node != null ? node.value : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) {
|
||||
return remove(node(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(K key) {
|
||||
return node(key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(V value) {
|
||||
if (size == 0) return false;
|
||||
Queue<Node<K, V>> queue = new LinkedList<>();
|
||||
for (int i = 0; i < table.length; i++) {
|
||||
if (table[i] == null) continue;
|
||||
|
||||
queue.offer(table[i]);
|
||||
while (!queue.isEmpty()) {
|
||||
Node<K, V> node = queue.poll();
|
||||
if (Objects.equals(value, node.value)) return true;
|
||||
|
||||
if (node.left != null) {
|
||||
queue.offer(node.left);
|
||||
}
|
||||
if (node.right != null) {
|
||||
queue.offer(node.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traversal(Visitor<K, V> visitor) {
|
||||
if (size == 0 || visitor == null) return;
|
||||
|
||||
Queue<Node<K, V>> queue = new LinkedList<>();
|
||||
for (int i = 0; i < table.length; i++) {
|
||||
if (table[i] == null) continue;
|
||||
|
||||
queue.offer(table[i]);
|
||||
while (!queue.isEmpty()) {
|
||||
Node<K, V> node = queue.poll();
|
||||
if (visitor.visit(node.key, node.value)) return;
|
||||
|
||||
if (node.left != null) {
|
||||
queue.offer(node.left);
|
||||
}
|
||||
if (node.right != null) {
|
||||
queue.offer(node.right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void print() {
|
||||
if (size == 0) return;
|
||||
for (int i = 0; i < table.length; i++) {
|
||||
final Node<K, V> root = table[i];
|
||||
System.out.println("【index = " + i + "】");
|
||||
BinaryTrees.println(new BinaryTreeInfo() {
|
||||
@Override
|
||||
public Object string(Object node) {
|
||||
return node;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object root() {
|
||||
return root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object right(Object node) {
|
||||
return ((Node<K, V>)node).right;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object left(Object node) {
|
||||
return ((Node<K, V>)node).left;
|
||||
}
|
||||
});
|
||||
System.out.println("---------------------------------------------------");
|
||||
}
|
||||
}
|
||||
|
||||
private V remove(Node<K, V> node) {
|
||||
if (node == null) return null;
|
||||
|
||||
size--;
|
||||
|
||||
V oldValue = node.value;
|
||||
|
||||
if (node.hasTwoChildren()) { // 度为2的节点
|
||||
// 找到后继节点
|
||||
Node<K, V> s = successor(node);
|
||||
// 用后继节点的值覆盖度为2的节点的值
|
||||
node.key = s.key;
|
||||
node.value = s.value;
|
||||
// 删除后继节点
|
||||
node = s;
|
||||
}
|
||||
|
||||
// 删除node节点(node的度必然是1或者0)
|
||||
Node<K, V> replacement = node.left != null ? node.left : node.right;
|
||||
int index = index(node);
|
||||
|
||||
if (replacement != null) { // node是度为1的节点
|
||||
// 更改parent
|
||||
replacement.parent = node.parent;
|
||||
// 更改parent的left、right的指向
|
||||
if (node.parent == null) { // node是度为1的节点并且是根节点
|
||||
table[index] = replacement;
|
||||
} else if (node == node.parent.left) {
|
||||
node.parent.left = replacement;
|
||||
} else { // node == node.parent.right
|
||||
node.parent.right = replacement;
|
||||
}
|
||||
|
||||
// 删除节点之后的处理
|
||||
afterRemove(replacement);
|
||||
} else if (node.parent == null) { // node是叶子节点并且是根节点
|
||||
table[index] = null;
|
||||
} else { // node是叶子节点,但不是根节点
|
||||
if (node == node.parent.left) {
|
||||
node.parent.left = null;
|
||||
} else { // node == node.parent.right
|
||||
node.parent.right = null;
|
||||
}
|
||||
|
||||
// 删除节点之后的处理
|
||||
afterRemove(node);
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
private Node<K, V> successor(Node<K, V> node) {
|
||||
if (node == null) return null;
|
||||
|
||||
// 前驱节点在左子树当中(right.left.left.left....)
|
||||
Node<K, V> p = node.right;
|
||||
if (p != null) {
|
||||
while (p.left != null) {
|
||||
p = p.left;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// 从父节点、祖父节点中寻找前驱节点
|
||||
while (node.parent != null && node == node.parent.right) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return node.parent;
|
||||
}
|
||||
|
||||
private Node<K, V> node(K key) {
|
||||
Node<K, V> root = table[index(key)];
|
||||
return root == null ? null : node(root, key);
|
||||
}
|
||||
|
||||
private Node<K, V> node(Node<K, V> node, K k1) {
|
||||
int h1 = k1 == null ? 0 : k1.hashCode();
|
||||
// 存储查找结果
|
||||
Node<K, V> result = null;
|
||||
int cmp = 0;
|
||||
while (node != null) {
|
||||
K k2 = node.key;
|
||||
int h2 = node.hash;
|
||||
// 先比较哈希值
|
||||
if (h1 > h2) {
|
||||
node = node.right;
|
||||
} else if (h1 < h2) {
|
||||
node = node.left;
|
||||
} else if (Objects.equals(k1, k2)) {
|
||||
return node;
|
||||
} else if (k1 != null && k2 != null
|
||||
&& k1.getClass() == k2.getClass()
|
||||
&& k1 instanceof Comparable
|
||||
&& (cmp = ((Comparable) k1).compareTo(k2)) != 0) {
|
||||
node = cmp > 0 ? node.right : node.left;
|
||||
} else if (node.right != null && (result = node(node.right, k1)) != null) {
|
||||
return result;
|
||||
} else { // 只能往左边找
|
||||
node = node.left;
|
||||
}
|
||||
// } else if (node.left != null && (result = node(node.left, k1)) != null) {
|
||||
// return result;
|
||||
// } else {
|
||||
// return null;
|
||||
// }
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key生成对应的索引(在桶数组中的位置)
|
||||
*/
|
||||
private int index(K key) {
|
||||
if (key == null) return 0;
|
||||
int hash = key.hashCode();
|
||||
return (hash ^ (hash >>> 16)) & (table.length - 1);
|
||||
}
|
||||
|
||||
private int index(Node<K, V> node) {
|
||||
return (node.hash ^ (node.hash >>> 16)) & (table.length - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较key大小
|
||||
* @param k1
|
||||
* @param k2
|
||||
* @param h1 k1的hashCode
|
||||
* @param h2 k2的hashCode
|
||||
* @return
|
||||
*/
|
||||
// private int compare(K k1, K k2, int h1, int h2) {
|
||||
// // 比较哈希值
|
||||
// int result = h1 - h2;
|
||||
// if (result != 0) return result;
|
||||
//
|
||||
// // 比较equals
|
||||
// if (Objects.equals(k1, k2)) return 0;
|
||||
//
|
||||
// // 哈希值相等,但是不equals
|
||||
// if (k1 != null && k2 != null
|
||||
// && k1.getClass() == k2.getClass()
|
||||
// && k1 instanceof Comparable) {
|
||||
// // 同一种类型并且具备可比较性
|
||||
// if (k1 instanceof Comparable) {
|
||||
// return ((Comparable) k1).compareTo(k2);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 同一种类型,哈希值相等,但是不equals,但是不具备可比较性
|
||||
// // k1不为null,k2为null
|
||||
// // k1为null,k2不为null
|
||||
// return System.identityHashCode(k1) - System.identityHashCode(k2);
|
||||
// }
|
||||
|
||||
private void afterRemove(Node<K, V> node) {
|
||||
// 如果删除的节点是红色
|
||||
// 或者 用以取代删除节点的子节点是红色
|
||||
if (isRed(node)) {
|
||||
black(node);
|
||||
return;
|
||||
}
|
||||
|
||||
Node<K, V> parent = node.parent;
|
||||
if (parent == null) return;
|
||||
|
||||
// 删除的是黑色叶子节点【下溢】
|
||||
// 判断被删除的node是左还是右
|
||||
boolean left = parent.left == null || node.isLeftChild();
|
||||
Node<K, V> sibling = left ? parent.right : parent.left;
|
||||
if (left) { // 被删除的节点在左边,兄弟节点在右边
|
||||
if (isRed(sibling)) { // 兄弟节点是红色
|
||||
black(sibling);
|
||||
red(parent);
|
||||
rotateLeft(parent);
|
||||
// 更换兄弟
|
||||
sibling = parent.right;
|
||||
}
|
||||
|
||||
// 兄弟节点必然是黑色
|
||||
if (isBlack(sibling.left) && isBlack(sibling.right)) {
|
||||
// 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
|
||||
boolean parentBlack = isBlack(parent);
|
||||
black(parent);
|
||||
red(sibling);
|
||||
if (parentBlack) {
|
||||
afterRemove(parent);
|
||||
}
|
||||
} else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
|
||||
// 兄弟节点的左边是黑色,兄弟要先旋转
|
||||
if (isBlack(sibling.right)) {
|
||||
rotateRight(sibling);
|
||||
sibling = parent.right;
|
||||
}
|
||||
|
||||
color(sibling, colorOf(parent));
|
||||
black(sibling.right);
|
||||
black(parent);
|
||||
rotateLeft(parent);
|
||||
}
|
||||
} else { // 被删除的节点在右边,兄弟节点在左边
|
||||
if (isRed(sibling)) { // 兄弟节点是红色
|
||||
black(sibling);
|
||||
red(parent);
|
||||
rotateRight(parent);
|
||||
// 更换兄弟
|
||||
sibling = parent.left;
|
||||
}
|
||||
|
||||
// 兄弟节点必然是黑色
|
||||
if (isBlack(sibling.left) && isBlack(sibling.right)) {
|
||||
// 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
|
||||
boolean parentBlack = isBlack(parent);
|
||||
black(parent);
|
||||
red(sibling);
|
||||
if (parentBlack) {
|
||||
afterRemove(parent);
|
||||
}
|
||||
} else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
|
||||
// 兄弟节点的左边是黑色,兄弟要先旋转
|
||||
if (isBlack(sibling.left)) {
|
||||
rotateLeft(sibling);
|
||||
sibling = parent.left;
|
||||
}
|
||||
|
||||
color(sibling, colorOf(parent));
|
||||
black(sibling.left);
|
||||
black(parent);
|
||||
rotateRight(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void afterPut(Node<K, V> node) {
|
||||
Node<K, V> parent = node.parent;
|
||||
|
||||
// 添加的是根节点 或者 上溢到达了根节点
|
||||
if (parent == null) {
|
||||
black(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果父节点是黑色,直接返回
|
||||
if (isBlack(parent)) return;
|
||||
|
||||
// 叔父节点
|
||||
Node<K, V> uncle = parent.sibling();
|
||||
// 祖父节点
|
||||
Node<K, V> grand = red(parent.parent);
|
||||
if (isRed(uncle)) { // 叔父节点是红色【B树节点上溢】
|
||||
black(parent);
|
||||
black(uncle);
|
||||
// 把祖父节点当做是新添加的节点
|
||||
afterPut(grand);
|
||||
return;
|
||||
}
|
||||
|
||||
// 叔父节点不是红色
|
||||
if (parent.isLeftChild()) { // L
|
||||
if (node.isLeftChild()) { // LL
|
||||
black(parent);
|
||||
} else { // LR
|
||||
black(node);
|
||||
rotateLeft(parent);
|
||||
}
|
||||
rotateRight(grand);
|
||||
} else { // R
|
||||
if (node.isLeftChild()) { // RL
|
||||
black(node);
|
||||
rotateRight(parent);
|
||||
} else { // RR
|
||||
black(parent);
|
||||
}
|
||||
rotateLeft(grand);
|
||||
}
|
||||
}
|
||||
|
||||
private void rotateLeft(Node<K, V> grand) {
|
||||
Node<K, V> parent = grand.right;
|
||||
Node<K, V> child = parent.left;
|
||||
grand.right = child;
|
||||
parent.left = grand;
|
||||
afterRotate(grand, parent, child);
|
||||
}
|
||||
|
||||
private void rotateRight(Node<K, V> grand) {
|
||||
Node<K, V> parent = grand.left;
|
||||
Node<K, V> child = parent.right;
|
||||
grand.left = child;
|
||||
parent.right = grand;
|
||||
afterRotate(grand, parent, child);
|
||||
}
|
||||
|
||||
private void afterRotate(Node<K, V> grand, Node<K, V> parent, Node<K, V> child) {
|
||||
// 让parent称为子树的根节点
|
||||
parent.parent = grand.parent;
|
||||
if (grand.isLeftChild()) {
|
||||
grand.parent.left = parent;
|
||||
} else if (grand.isRightChild()) {
|
||||
grand.parent.right = parent;
|
||||
} else { // grand是root节点
|
||||
table[index(grand)] = parent;
|
||||
}
|
||||
|
||||
// 更新child的parent
|
||||
if (child != null) {
|
||||
child.parent = grand;
|
||||
}
|
||||
|
||||
// 更新grand的parent
|
||||
grand.parent = parent;
|
||||
}
|
||||
|
||||
private Node<K, V> color(Node<K, V> node, boolean color) {
|
||||
if (node == null) return node;
|
||||
node.color = color;
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node<K, V> red(Node<K, V> node) {
|
||||
return color(node, RED);
|
||||
}
|
||||
|
||||
private Node<K, V> black(Node<K, V> node) {
|
||||
return color(node, BLACK);
|
||||
}
|
||||
|
||||
private boolean colorOf(Node<K, V> node) {
|
||||
return node == null ? BLACK : node.color;
|
||||
}
|
||||
|
||||
private boolean isBlack(Node<K, V> node) {
|
||||
return colorOf(node) == BLACK;
|
||||
}
|
||||
|
||||
private boolean isRed(Node<K, V> node) {
|
||||
return colorOf(node) == RED;
|
||||
}
|
||||
|
||||
private static class Node<K, V> {
|
||||
int hash;
|
||||
K key;
|
||||
V value;
|
||||
boolean color = RED;
|
||||
Node<K, V> left;
|
||||
Node<K, V> right;
|
||||
Node<K, V> parent;
|
||||
public Node(K key, V value, Node<K, V> parent) {
|
||||
this.key = key;
|
||||
this.hash = key == null ? 0 : key.hashCode();
|
||||
this.value = value;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public boolean hasTwoChildren() {
|
||||
return left != null && right != null;
|
||||
}
|
||||
|
||||
public boolean isLeftChild() {
|
||||
return parent != null && this == parent.left;
|
||||
}
|
||||
|
||||
public boolean isRightChild() {
|
||||
return parent != null && this == parent.right;
|
||||
}
|
||||
|
||||
public Node<K, V> sibling() {
|
||||
if (isLeftChild()) {
|
||||
return parent.right;
|
||||
}
|
||||
|
||||
if (isRightChild()) {
|
||||
return parent.left;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Node_" + key + "_" + value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.mj.map;
|
||||
|
||||
public interface Map<K, V> {
|
||||
int size();
|
||||
boolean isEmpty();
|
||||
void clear();
|
||||
V put(K key, V value);
|
||||
V get(K key);
|
||||
V remove(K key);
|
||||
boolean containsKey(K key);
|
||||
boolean containsValue(V value);
|
||||
void traversal(Visitor<K, V> visitor);
|
||||
|
||||
public static abstract class Visitor<K, V> {
|
||||
boolean stop;
|
||||
public abstract boolean visit(K key, V value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,486 @@
|
||||
package com.mj.map;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Queue;
|
||||
|
||||
@SuppressWarnings({"unchecked", "unused"})
|
||||
public class TreeMap<K, V> implements Map<K, V> {
|
||||
private static final boolean RED = false;
|
||||
private static final boolean BLACK = true;
|
||||
private int size;
|
||||
private Node<K, V> root;
|
||||
private Comparator<K> comparator;
|
||||
|
||||
public TreeMap() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public TreeMap(Comparator<K> comparator) {
|
||||
this.comparator = comparator;
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
root = null;
|
||||
size = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V put(K key, V value) {
|
||||
keyNotNullCheck(key);
|
||||
|
||||
// 添加第一个节点
|
||||
if (root == null) {
|
||||
root = new Node<>(key, value, null);
|
||||
size++;
|
||||
|
||||
// 新添加节点之后的处理
|
||||
afterPut(root);
|
||||
return null;
|
||||
}
|
||||
|
||||
// 添加的不是第一个节点
|
||||
// 找到父节点
|
||||
Node<K, V> parent = root;
|
||||
Node<K, V> node = root;
|
||||
int cmp = 0;
|
||||
do {
|
||||
cmp = compare(key, node.key);
|
||||
parent = node;
|
||||
if (cmp > 0) {
|
||||
node = node.right;
|
||||
} else if (cmp < 0) {
|
||||
node = node.left;
|
||||
} else { // 相等
|
||||
node.key = key;
|
||||
V oldValue = node.value;
|
||||
node.value = value;
|
||||
return oldValue;
|
||||
}
|
||||
} while (node != null);
|
||||
|
||||
// 看看插入到父节点的哪个位置
|
||||
Node<K, V> newNode = new Node<>(key, value, parent);
|
||||
if (cmp > 0) {
|
||||
parent.right = newNode;
|
||||
} else {
|
||||
parent.left = newNode;
|
||||
}
|
||||
size++;
|
||||
|
||||
// 新添加节点之后的处理
|
||||
afterPut(newNode);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(K key) {
|
||||
Node<K, V> node = node(key);
|
||||
return node != null ? node.value : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V remove(K key) {
|
||||
return remove(node(key));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(K key) {
|
||||
return node(key) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(V value) {
|
||||
if (root == null) return false;
|
||||
|
||||
Queue<Node<K, V>> queue = new LinkedList<>();
|
||||
queue.offer(root);
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
Node<K, V> node = queue.poll();
|
||||
if (valEquals(value, node.value)) return true;
|
||||
|
||||
if (node.left != null) {
|
||||
queue.offer(node.left);
|
||||
}
|
||||
|
||||
if (node.right != null) {
|
||||
queue.offer(node.right);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void traversal(Visitor<K, V> visitor) {
|
||||
if (visitor == null) return;
|
||||
traversal(root, visitor);
|
||||
}
|
||||
|
||||
private void traversal(Node<K, V> node, Visitor<K, V> visitor) {
|
||||
if (node == null || visitor.stop) return;
|
||||
|
||||
traversal(node.left, visitor);
|
||||
if (visitor.stop) return;
|
||||
visitor.visit(node.key, node.value);
|
||||
traversal(node.right, visitor);
|
||||
}
|
||||
|
||||
private boolean valEquals(V v1, V v2) {
|
||||
return v1 == null ? v2 == null : v1.equals(v2);
|
||||
}
|
||||
|
||||
private V remove(Node<K, V> node) {
|
||||
if (node == null) return null;
|
||||
|
||||
size--;
|
||||
|
||||
V oldValue = node.value;
|
||||
|
||||
if (node.hasTwoChildren()) { // 度为2的节点
|
||||
// 找到后继节点
|
||||
Node<K, V> s = successor(node);
|
||||
// 用后继节点的值覆盖度为2的节点的值
|
||||
node.key = s.key;
|
||||
node.value = s.value;
|
||||
// 删除后继节点
|
||||
node = s;
|
||||
}
|
||||
|
||||
// 删除node节点(node的度必然是1或者0)
|
||||
Node<K, V> replacement = node.left != null ? node.left : node.right;
|
||||
|
||||
if (replacement != null) { // node是度为1的节点
|
||||
// 更改parent
|
||||
replacement.parent = node.parent;
|
||||
// 更改parent的left、right的指向
|
||||
if (node.parent == null) { // node是度为1的节点并且是根节点
|
||||
root = replacement;
|
||||
} else if (node == node.parent.left) {
|
||||
node.parent.left = replacement;
|
||||
} else { // node == node.parent.right
|
||||
node.parent.right = replacement;
|
||||
}
|
||||
|
||||
// 删除节点之后的处理
|
||||
afterRemove(replacement);
|
||||
} else if (node.parent == null) { // node是叶子节点并且是根节点
|
||||
root = null;
|
||||
} else { // node是叶子节点,但不是根节点
|
||||
if (node == node.parent.left) {
|
||||
node.parent.left = null;
|
||||
} else { // node == node.parent.right
|
||||
node.parent.right = null;
|
||||
}
|
||||
|
||||
// 删除节点之后的处理
|
||||
afterRemove(node);
|
||||
}
|
||||
|
||||
return oldValue;
|
||||
}
|
||||
|
||||
private void afterRemove(Node<K, V> node) {
|
||||
// 如果删除的节点是红色
|
||||
// 或者 用以取代删除节点的子节点是红色
|
||||
if (isRed(node)) {
|
||||
black(node);
|
||||
return;
|
||||
}
|
||||
|
||||
Node<K, V> parent = node.parent;
|
||||
if (parent == null) return;
|
||||
|
||||
// 删除的是黑色叶子节点【下溢】
|
||||
// 判断被删除的node是左还是右
|
||||
boolean left = parent.left == null || node.isLeftChild();
|
||||
Node<K, V> sibling = left ? parent.right : parent.left;
|
||||
if (left) { // 被删除的节点在左边,兄弟节点在右边
|
||||
if (isRed(sibling)) { // 兄弟节点是红色
|
||||
black(sibling);
|
||||
red(parent);
|
||||
rotateLeft(parent);
|
||||
// 更换兄弟
|
||||
sibling = parent.right;
|
||||
}
|
||||
|
||||
// 兄弟节点必然是黑色
|
||||
if (isBlack(sibling.left) && isBlack(sibling.right)) {
|
||||
// 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
|
||||
boolean parentBlack = isBlack(parent);
|
||||
black(parent);
|
||||
red(sibling);
|
||||
if (parentBlack) {
|
||||
afterRemove(parent);
|
||||
}
|
||||
} else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
|
||||
// 兄弟节点的左边是黑色,兄弟要先旋转
|
||||
if (isBlack(sibling.right)) {
|
||||
rotateRight(sibling);
|
||||
sibling = parent.right;
|
||||
}
|
||||
|
||||
color(sibling, colorOf(parent));
|
||||
black(sibling.right);
|
||||
black(parent);
|
||||
rotateLeft(parent);
|
||||
}
|
||||
} else { // 被删除的节点在右边,兄弟节点在左边
|
||||
if (isRed(sibling)) { // 兄弟节点是红色
|
||||
black(sibling);
|
||||
red(parent);
|
||||
rotateRight(parent);
|
||||
// 更换兄弟
|
||||
sibling = parent.left;
|
||||
}
|
||||
|
||||
// 兄弟节点必然是黑色
|
||||
if (isBlack(sibling.left) && isBlack(sibling.right)) {
|
||||
// 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
|
||||
boolean parentBlack = isBlack(parent);
|
||||
black(parent);
|
||||
red(sibling);
|
||||
if (parentBlack) {
|
||||
afterRemove(parent);
|
||||
}
|
||||
} else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
|
||||
// 兄弟节点的左边是黑色,兄弟要先旋转
|
||||
if (isBlack(sibling.left)) {
|
||||
rotateLeft(sibling);
|
||||
sibling = parent.left;
|
||||
}
|
||||
|
||||
color(sibling, colorOf(parent));
|
||||
black(sibling.left);
|
||||
black(parent);
|
||||
rotateRight(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Node<K, V> predecessor(Node<K, V> node) {
|
||||
if (node == null) return null;
|
||||
|
||||
// 前驱节点在左子树当中(left.right.right.right....)
|
||||
Node<K, V> p = node.left;
|
||||
if (p != null) {
|
||||
while (p.right != null) {
|
||||
p = p.right;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// 从父节点、祖父节点中寻找前驱节点
|
||||
while (node.parent != null && node == node.parent.left) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
// node.parent == null
|
||||
// node == node.parent.right
|
||||
return node.parent;
|
||||
}
|
||||
|
||||
private Node<K, V> successor(Node<K, V> node) {
|
||||
if (node == null) return null;
|
||||
|
||||
// 前驱节点在左子树当中(right.left.left.left....)
|
||||
Node<K, V> p = node.right;
|
||||
if (p != null) {
|
||||
while (p.left != null) {
|
||||
p = p.left;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// 从父节点、祖父节点中寻找前驱节点
|
||||
while (node.parent != null && node == node.parent.right) {
|
||||
node = node.parent;
|
||||
}
|
||||
|
||||
return node.parent;
|
||||
}
|
||||
|
||||
private Node<K, V> node(K key) {
|
||||
Node<K, V> node = root;
|
||||
while (node != null) {
|
||||
int cmp = compare(key, node.key);
|
||||
if (cmp == 0) return node;
|
||||
if (cmp > 0) {
|
||||
node = node.right;
|
||||
} else { // cmp < 0
|
||||
node = node.left;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void afterPut(Node<K, V> node) {
|
||||
Node<K, V> parent = node.parent;
|
||||
|
||||
// 添加的是根节点 或者 上溢到达了根节点
|
||||
if (parent == null) {
|
||||
black(node);
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果父节点是黑色,直接返回
|
||||
if (isBlack(parent)) return;
|
||||
|
||||
// 叔父节点
|
||||
Node<K, V> uncle = parent.sibling();
|
||||
// 祖父节点
|
||||
Node<K, V> grand = red(parent.parent);
|
||||
if (isRed(uncle)) { // 叔父节点是红色【B树节点上溢】
|
||||
black(parent);
|
||||
black(uncle);
|
||||
// 把祖父节点当做是新添加的节点
|
||||
afterPut(grand);
|
||||
return;
|
||||
}
|
||||
|
||||
// 叔父节点不是红色
|
||||
if (parent.isLeftChild()) { // L
|
||||
if (node.isLeftChild()) { // LL
|
||||
black(parent);
|
||||
} else { // LR
|
||||
black(node);
|
||||
rotateLeft(parent);
|
||||
}
|
||||
rotateRight(grand);
|
||||
} else { // R
|
||||
if (node.isLeftChild()) { // RL
|
||||
black(node);
|
||||
rotateRight(parent);
|
||||
} else { // RR
|
||||
black(parent);
|
||||
}
|
||||
rotateLeft(grand);
|
||||
}
|
||||
}
|
||||
|
||||
private void rotateLeft(Node<K, V> grand) {
|
||||
Node<K, V> parent = grand.right;
|
||||
Node<K, V> child = parent.left;
|
||||
grand.right = child;
|
||||
parent.left = grand;
|
||||
afterRotate(grand, parent, child);
|
||||
}
|
||||
|
||||
private void rotateRight(Node<K, V> grand) {
|
||||
Node<K, V> parent = grand.left;
|
||||
Node<K, V> child = parent.right;
|
||||
grand.left = child;
|
||||
parent.right = grand;
|
||||
afterRotate(grand, parent, child);
|
||||
}
|
||||
|
||||
private void afterRotate(Node<K, V> grand, Node<K, V> parent, Node<K, V> child) {
|
||||
// 让parent称为子树的根节点
|
||||
parent.parent = grand.parent;
|
||||
if (grand.isLeftChild()) {
|
||||
grand.parent.left = parent;
|
||||
} else if (grand.isRightChild()) {
|
||||
grand.parent.right = parent;
|
||||
} else { // grand是root节点
|
||||
root = parent;
|
||||
}
|
||||
|
||||
// 更新child的parent
|
||||
if (child != null) {
|
||||
child.parent = grand;
|
||||
}
|
||||
|
||||
// 更新grand的parent
|
||||
grand.parent = parent;
|
||||
}
|
||||
|
||||
private Node<K, V> color(Node<K, V> node, boolean color) {
|
||||
if (node == null) return node;
|
||||
node.color = color;
|
||||
return node;
|
||||
}
|
||||
|
||||
private Node<K, V> red(Node<K, V> node) {
|
||||
return color(node, RED);
|
||||
}
|
||||
|
||||
private Node<K, V> black(Node<K, V> node) {
|
||||
return color(node, BLACK);
|
||||
}
|
||||
|
||||
private boolean colorOf(Node<K, V> node) {
|
||||
return node == null ? BLACK : node.color;
|
||||
}
|
||||
|
||||
private boolean isBlack(Node<K, V> node) {
|
||||
return colorOf(node) == BLACK;
|
||||
}
|
||||
|
||||
private boolean isRed(Node<K, V> node) {
|
||||
return colorOf(node) == RED;
|
||||
}
|
||||
|
||||
private int compare(K e1, K e2) {
|
||||
if (comparator != null) {
|
||||
return comparator.compare(e1, e2);
|
||||
}
|
||||
return ((Comparable<K>)e1).compareTo(e2);
|
||||
}
|
||||
|
||||
private void keyNotNullCheck(K key) {
|
||||
if (key == null) {
|
||||
throw new IllegalArgumentException("key must not be null");
|
||||
}
|
||||
}
|
||||
|
||||
private static class Node<K, V> {
|
||||
K key;
|
||||
V value;
|
||||
boolean color = RED;
|
||||
Node<K, V> left;
|
||||
Node<K, V> right;
|
||||
Node<K, V> parent;
|
||||
public Node(K key, V value, Node<K, V> parent) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public boolean isLeaf() {
|
||||
return left == null && right == null;
|
||||
}
|
||||
|
||||
public boolean hasTwoChildren() {
|
||||
return left != null && right != null;
|
||||
}
|
||||
|
||||
public boolean isLeftChild() {
|
||||
return parent != null && this == parent.left;
|
||||
}
|
||||
|
||||
public boolean isRightChild() {
|
||||
return parent != null && this == parent.right;
|
||||
}
|
||||
|
||||
public Node<K, V> sibling() {
|
||||
if (isLeftChild()) {
|
||||
return parent.right;
|
||||
}
|
||||
|
||||
if (isRightChild()) {
|
||||
return parent.left;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.mj.model;
|
||||
|
||||
public class Key {
|
||||
protected int value;
|
||||
|
||||
public Key(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return value / 10;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != getClass()) return false;
|
||||
return ((Key) obj).value == value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "v(" + value + ")";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.mj.model;
|
||||
|
||||
public class Person implements Comparable<Person> {
|
||||
private int age; // 10 20
|
||||
private float height; // 1.55 1.67
|
||||
private String name; // "jack" "rose"
|
||||
|
||||
public Person(int age, float height, String name) {
|
||||
this.age = age;
|
||||
this.height = height;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* 用来比较2个对象是否相等
|
||||
*/
|
||||
public boolean equals(Object obj) {
|
||||
// 内存地址
|
||||
if (this == obj) return true;
|
||||
if (obj == null || obj.getClass() != getClass()) return false;
|
||||
// if (obj == null || !(obj instanceof Person)) return false;
|
||||
|
||||
// 比较成员变量
|
||||
Person person = (Person) obj;
|
||||
return person.age == age
|
||||
&& person.height == height
|
||||
&& (person.name == null ? name == null : person.name.equals(name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = Integer.hashCode(age);
|
||||
hashCode = hashCode * 31 + Float.hashCode(height);
|
||||
hashCode = hashCode * 31 + (name != null ? name.hashCode() : 0);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Person o) {
|
||||
return age - o.age;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.mj.model;
|
||||
|
||||
public class Student extends Person {
|
||||
|
||||
public Student(int age, float height, String name) {
|
||||
super(age, height, name);
|
||||
// TODO Auto-generated constructor stub
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mj.model;
|
||||
|
||||
public class SubKey1 extends Key {
|
||||
|
||||
public SubKey1(int value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null ||
|
||||
(obj.getClass() != SubKey1.class
|
||||
&& obj.getClass() != SubKey2.class)) return false;
|
||||
return ((Key) obj).value == value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mj.model;
|
||||
|
||||
public class SubKey2 extends Key {
|
||||
|
||||
public SubKey2(int value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null ||
|
||||
(obj.getClass() != SubKey1.class
|
||||
&& obj.getClass() != SubKey2.class)) return false;
|
||||
return ((Key) obj).value == value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.mj.printer;
|
||||
|
||||
public interface BinaryTreeInfo {
|
||||
/**
|
||||
* who is the root node
|
||||
*/
|
||||
Object root();
|
||||
/**
|
||||
* how to get the left child of the node
|
||||
*/
|
||||
Object left(Object node);
|
||||
/**
|
||||
* how to get the right child of the node
|
||||
*/
|
||||
Object right(Object node);
|
||||
/**
|
||||
* how to print the node
|
||||
*/
|
||||
Object string(Object node);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.mj.printer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author MJ Lee
|
||||
*
|
||||
*/
|
||||
public final class BinaryTrees {
|
||||
|
||||
private BinaryTrees() {
|
||||
}
|
||||
|
||||
public static void print(BinaryTreeInfo tree) {
|
||||
print(tree, null);
|
||||
}
|
||||
|
||||
public static void println(BinaryTreeInfo tree) {
|
||||
println(tree, null);
|
||||
}
|
||||
|
||||
public static void print(BinaryTreeInfo tree, PrintStyle style) {
|
||||
if (tree == null || tree.root() == null) return;
|
||||
printer(tree, style).print();
|
||||
}
|
||||
|
||||
public static void println(BinaryTreeInfo tree, PrintStyle style) {
|
||||
if (tree == null || tree.root() == null) return;
|
||||
printer(tree, style).println();
|
||||
}
|
||||
|
||||
public static String printString(BinaryTreeInfo tree) {
|
||||
return printString(tree, null);
|
||||
}
|
||||
|
||||
public static String printString(BinaryTreeInfo tree, PrintStyle style) {
|
||||
if (tree == null || tree.root() == null) return null;
|
||||
return printer(tree, style).printString();
|
||||
}
|
||||
|
||||
private static Printer printer(BinaryTreeInfo tree, PrintStyle style) {
|
||||
if (style == PrintStyle.INORDER) return new InorderPrinter(tree);
|
||||
return new LevelOrderPrinter(tree);
|
||||
}
|
||||
|
||||
public enum PrintStyle {
|
||||
LEVEL_ORDER, INORDER
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.mj.printer;
|
||||
|
||||
/**
|
||||
|
||||
┌──800
|
||||
┌──760
|
||||
│ └──600
|
||||
┌──540
|
||||
│ └──476
|
||||
│ └──445
|
||||
┌──410
|
||||
│ └──394
|
||||
381
|
||||
│ ┌──190
|
||||
│ │ └──146
|
||||
│ ┌──40
|
||||
│ │ └──35
|
||||
└──12
|
||||
└──9
|
||||
|
||||
* @author MJ Lee
|
||||
*
|
||||
*/
|
||||
public class InorderPrinter extends Printer {
|
||||
private static String rightAppend;
|
||||
private static String leftAppend;
|
||||
private static String blankAppend;
|
||||
private static String lineAppend;
|
||||
static {
|
||||
int length = 2;
|
||||
rightAppend = "┌" + Strings.repeat("─", length);
|
||||
leftAppend = "└" + Strings.repeat("─", length);
|
||||
blankAppend = Strings.blank(length + 1);
|
||||
lineAppend = "│" + Strings.blank(length);
|
||||
}
|
||||
|
||||
public InorderPrinter(BinaryTreeInfo tree) {
|
||||
super(tree);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String printString() {
|
||||
StringBuilder string = new StringBuilder(
|
||||
printString(tree.root(), "", "", ""));
|
||||
string.deleteCharAt(string.length() - 1);
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成node节点的字符串
|
||||
* @param nodePrefix node那一行的前缀字符串
|
||||
* @param leftPrefix node整棵左子树的前缀字符串
|
||||
* @param rightPrefix node整棵右子树的前缀字符串
|
||||
* @return
|
||||
*/
|
||||
private String printString(
|
||||
Object node,
|
||||
String nodePrefix,
|
||||
String leftPrefix,
|
||||
String rightPrefix) {
|
||||
Object left = tree.left(node);
|
||||
Object right = tree.right(node);
|
||||
String string = tree.string(node).toString();
|
||||
|
||||
int length = string.length();
|
||||
if (length % 2 == 0) {
|
||||
length--;
|
||||
}
|
||||
length >>= 1;
|
||||
|
||||
String nodeString = "";
|
||||
if (right != null) {
|
||||
rightPrefix += Strings.blank(length);
|
||||
nodeString += printString(right,
|
||||
rightPrefix + rightAppend,
|
||||
rightPrefix + lineAppend,
|
||||
rightPrefix + blankAppend);
|
||||
}
|
||||
nodeString += nodePrefix + string + "\n";
|
||||
if (left != null) {
|
||||
leftPrefix += Strings.blank(length);
|
||||
nodeString += printString(left,
|
||||
leftPrefix + leftAppend,
|
||||
leftPrefix + blankAppend,
|
||||
leftPrefix + lineAppend);
|
||||
}
|
||||
return nodeString;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
package com.mj.printer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
|
||||
┌───381────┐
|
||||
│ │
|
||||
┌─12─┐ ┌─410─┐
|
||||
│ │ │ │
|
||||
9 ┌─40─┐ 394 ┌─540─┐
|
||||
│ │ │ │
|
||||
35 ┌─190 ┌─476 ┌─760─┐
|
||||
│ │ │ │
|
||||
146 445 600 800
|
||||
|
||||
* @author MJ Lee
|
||||
*
|
||||
*/
|
||||
public class LevelOrderPrinter extends Printer {
|
||||
/**
|
||||
* 节点之间允许的最小间距(最小只能填1)
|
||||
*/
|
||||
private static final int MIN_SPACE = 1;
|
||||
private Node root;
|
||||
private int minX;
|
||||
private int maxWidth;
|
||||
|
||||
public LevelOrderPrinter(BinaryTreeInfo tree) {
|
||||
super(tree);
|
||||
|
||||
root = new Node(tree.root(), tree);
|
||||
maxWidth = root.width;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String printString() {
|
||||
// nodes用来存放所有的节点
|
||||
List<List<Node>> nodes = new ArrayList<>();
|
||||
fillNodes(nodes);
|
||||
cleanNodes(nodes);
|
||||
compressNodes(nodes);
|
||||
addLineNodes(nodes);
|
||||
|
||||
int rowCount = nodes.size();
|
||||
|
||||
// 构建字符串
|
||||
StringBuilder string = new StringBuilder();
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
if (i != 0) {
|
||||
string.append("\n");
|
||||
}
|
||||
|
||||
List<Node> rowNodes = nodes.get(i);
|
||||
StringBuilder rowSb = new StringBuilder();
|
||||
for (Node node : rowNodes) {
|
||||
int leftSpace = node.x - rowSb.length() - minX;
|
||||
rowSb.append(Strings.blank(leftSpace));
|
||||
rowSb.append(node.string);
|
||||
}
|
||||
|
||||
string.append(rowSb);
|
||||
}
|
||||
|
||||
return string.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一个元素节点
|
||||
*/
|
||||
private Node addNode(List<Node> nodes, Object btNode) {
|
||||
Node node = null;
|
||||
if (btNode != null) {
|
||||
node = new Node(btNode, tree);
|
||||
maxWidth = Math.max(maxWidth, node.width);
|
||||
nodes.add(node);
|
||||
} else {
|
||||
nodes.add(null);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* 以满二叉树的形式填充节点
|
||||
*/
|
||||
private void fillNodes(List<List<Node>> nodes) {
|
||||
if (nodes == null) return;
|
||||
// 第一行
|
||||
List<Node> firstRowNodes = new ArrayList<>();
|
||||
firstRowNodes.add(root);
|
||||
nodes.add(firstRowNodes);
|
||||
|
||||
// 其他行
|
||||
while (true) {
|
||||
List<Node> preRowNodes = nodes.get(nodes.size() - 1);
|
||||
List<Node> rowNodes = new ArrayList<>();
|
||||
|
||||
boolean notNull = false;
|
||||
for (Node node : preRowNodes) {
|
||||
if (node == null) {
|
||||
rowNodes.add(null);
|
||||
rowNodes.add(null);
|
||||
} else {
|
||||
Node left = addNode(rowNodes, tree.left(node.btNode));
|
||||
if (left != null) {
|
||||
node.left = left;
|
||||
left.parent = node;
|
||||
notNull = true;
|
||||
}
|
||||
|
||||
Node right = addNode(rowNodes, tree.right(node.btNode));
|
||||
if (right != null) {
|
||||
node.right = right;
|
||||
right.parent = node;
|
||||
notNull = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 全是null,就退出
|
||||
if (!notNull) break;
|
||||
nodes.add(rowNodes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除全部null、更新节点的坐标
|
||||
*/
|
||||
private void cleanNodes(List<List<Node>> nodes) {
|
||||
if (nodes == null) return;
|
||||
|
||||
int rowCount = nodes.size();
|
||||
if (rowCount < 2) return;
|
||||
|
||||
// 最后一行的节点数量
|
||||
int lastRowNodeCount = nodes.get(rowCount - 1).size();
|
||||
|
||||
// 每个节点之间的间距
|
||||
int nodeSpace = maxWidth + 2;
|
||||
|
||||
// 最后一行的长度
|
||||
int lastRowLength = lastRowNodeCount * maxWidth
|
||||
+ nodeSpace * (lastRowNodeCount - 1);
|
||||
|
||||
// 空集合
|
||||
Collection<Object> nullSet = Collections.singleton(null);
|
||||
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
List<Node> rowNodes = nodes.get(i);
|
||||
|
||||
int rowNodeCount = rowNodes.size();
|
||||
// 节点左右两边的间距
|
||||
int allSpace = lastRowLength - (rowNodeCount - 1) * nodeSpace;
|
||||
int cornerSpace = allSpace / rowNodeCount - maxWidth;
|
||||
cornerSpace >>= 1;
|
||||
|
||||
int rowLength = 0;
|
||||
for (int j = 0; j < rowNodeCount; j++) {
|
||||
if (j != 0) {
|
||||
// 每个节点之间的间距
|
||||
rowLength += nodeSpace;
|
||||
}
|
||||
rowLength += cornerSpace;
|
||||
Node node = rowNodes.get(j);
|
||||
if (node != null) {
|
||||
// 居中(由于奇偶数的问题,可能有1个符号的误差)
|
||||
int deltaX = (maxWidth - node.width) >> 1;
|
||||
node.x = rowLength + deltaX;
|
||||
node.y = i;
|
||||
}
|
||||
rowLength += maxWidth;
|
||||
rowLength += cornerSpace;
|
||||
}
|
||||
// 删除所有的null
|
||||
rowNodes.removeAll(nullSet);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 压缩空格
|
||||
*/
|
||||
private void compressNodes(List<List<Node>> nodes) {
|
||||
if (nodes == null) return;
|
||||
|
||||
int rowCount = nodes.size();
|
||||
if (rowCount < 2) return;
|
||||
|
||||
for (int i = rowCount - 2; i >= 0; i--) {
|
||||
List<Node> rowNodes = nodes.get(i);
|
||||
for (Node node : rowNodes) {
|
||||
Node left = node.left;
|
||||
Node right = node.right;
|
||||
if (left == null && right == null) continue;
|
||||
if (left != null && right != null) {
|
||||
// 让左右节点对称
|
||||
node.balance(left, right);
|
||||
|
||||
// left和right之间可以挪动的最小间距
|
||||
int leftEmpty = node.leftBoundEmptyLength();
|
||||
int rightEmpty = node.rightBoundEmptyLength();
|
||||
int empty = Math.min(leftEmpty, rightEmpty);
|
||||
empty = Math.min(empty, (right.x - left.rightX()) >> 1);
|
||||
|
||||
// left、right的子节点之间可以挪动的最小间距
|
||||
int space = left.minLevelSpaceToRight(right) - MIN_SPACE;
|
||||
space = Math.min(space >> 1, empty);
|
||||
|
||||
// left、right往中间挪动
|
||||
if (space > 0) {
|
||||
left.translateX(space);
|
||||
right.translateX(-space);
|
||||
}
|
||||
|
||||
// 继续挪动
|
||||
space = left.minLevelSpaceToRight(right) - MIN_SPACE;
|
||||
if (space < 1) continue;
|
||||
|
||||
// 可以继续挪动的间距
|
||||
leftEmpty = node.leftBoundEmptyLength();
|
||||
rightEmpty = node.rightBoundEmptyLength();
|
||||
if (leftEmpty < 1 && rightEmpty < 1) continue;
|
||||
|
||||
if (leftEmpty > rightEmpty) {
|
||||
left.translateX(Math.min(leftEmpty, space));
|
||||
} else {
|
||||
right.translateX(-Math.min(rightEmpty, space));
|
||||
}
|
||||
} else if (left != null) {
|
||||
left.translateX(node.leftBoundEmptyLength());
|
||||
} else { // right != null
|
||||
right.translateX(-node.rightBoundEmptyLength());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addXLineNode(List<Node> curRow, Node parent, int x) {
|
||||
Node line = new Node("─");
|
||||
line.x = x;
|
||||
line.y = parent.y;
|
||||
curRow.add(line);
|
||||
}
|
||||
|
||||
private Node addLineNode(List<Node> curRow, List<Node> nextRow, Node parent, Node child) {
|
||||
if (child == null) return null;
|
||||
|
||||
Node top = null;
|
||||
int topX = child.topLineX();
|
||||
if (child == parent.left) {
|
||||
top = new Node("┌");
|
||||
curRow.add(top);
|
||||
|
||||
for (int x = topX + 1; x < parent.x; x++) {
|
||||
addXLineNode(curRow, parent, x);
|
||||
}
|
||||
} else {
|
||||
for (int x = parent.rightX(); x < topX; x++) {
|
||||
addXLineNode(curRow, parent, x);
|
||||
}
|
||||
|
||||
top = new Node("┐");
|
||||
curRow.add(top);
|
||||
}
|
||||
|
||||
// 坐标
|
||||
top.x = topX;
|
||||
top.y = parent.y;
|
||||
child.y = parent.y + 2;
|
||||
minX = Math.min(minX, child.x);
|
||||
|
||||
// 竖线
|
||||
Node bottom = new Node("│");
|
||||
bottom.x = topX;
|
||||
bottom.y = parent.y + 1;
|
||||
nextRow.add(bottom);
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
private void addLineNodes(List<List<Node>> nodes) {
|
||||
List<List<Node>> newNodes = new ArrayList<>();
|
||||
|
||||
int rowCount = nodes.size();
|
||||
if (rowCount < 2) return;
|
||||
|
||||
minX = root.x;
|
||||
|
||||
for (int i = 0; i < rowCount; i++) {
|
||||
List<Node> rowNodes = nodes.get(i);
|
||||
if (i == rowCount - 1) {
|
||||
newNodes.add(rowNodes);
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Node> newRowNodes = new ArrayList<>();
|
||||
newNodes.add(newRowNodes);
|
||||
|
||||
List<Node> lineNodes = new ArrayList<>();
|
||||
newNodes.add(lineNodes);
|
||||
for (Node node : rowNodes) {
|
||||
addLineNode(newRowNodes, lineNodes, node, node.left);
|
||||
newRowNodes.add(node);
|
||||
addLineNode(newRowNodes, lineNodes, node, node.right);
|
||||
}
|
||||
}
|
||||
|
||||
nodes.clear();
|
||||
nodes.addAll(newNodes);
|
||||
}
|
||||
|
||||
private static class Node {
|
||||
/**
|
||||
* 顶部符号距离父节点的最小距离(最小能填0)
|
||||
*/
|
||||
private static final int TOP_LINE_SPACE = 1;
|
||||
|
||||
Object btNode;
|
||||
Node left;
|
||||
Node right;
|
||||
Node parent;
|
||||
/**
|
||||
* 首字符的位置
|
||||
*/
|
||||
int x;
|
||||
int y;
|
||||
int treeHeight;
|
||||
String string;
|
||||
int width;
|
||||
|
||||
private void init(String string) {
|
||||
string = (string == null) ? "null" : string;
|
||||
string = string.isEmpty() ? " " : string;
|
||||
|
||||
width = string.length();
|
||||
this.string = string;
|
||||
}
|
||||
|
||||
public Node(String string) {
|
||||
init(string);
|
||||
}
|
||||
|
||||
public Node(Object btNode, BinaryTreeInfo opetaion) {
|
||||
init(opetaion.string(btNode).toString());
|
||||
|
||||
this.btNode = btNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 顶部方向字符的X(极其重要)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private int topLineX() {
|
||||
// 宽度的一半
|
||||
int delta = width;
|
||||
if (delta % 2 == 0) {
|
||||
delta--;
|
||||
}
|
||||
delta >>= 1;
|
||||
|
||||
if (parent != null && this == parent.left) {
|
||||
return rightX() - 1 - delta;
|
||||
} else {
|
||||
return x + delta;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 右边界的位置(rightX 或者 右子节点topLineX的下一个位置)(极其重要)
|
||||
*/
|
||||
private int rightBound() {
|
||||
if (right == null) return rightX();
|
||||
return right.topLineX() + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 左边界的位置(x 或者 左子节点topLineX)(极其重要)
|
||||
*/
|
||||
private int leftBound() {
|
||||
if (left == null) return x;
|
||||
return left.topLineX();
|
||||
}
|
||||
|
||||
/**
|
||||
* x ~ 左边界之间的长度(包括左边界字符)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private int leftBoundLength() {
|
||||
return x - leftBound();
|
||||
}
|
||||
|
||||
/**
|
||||
* rightX ~ 右边界之间的长度(包括右边界字符)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private int rightBoundLength() {
|
||||
return rightBound() - rightX();
|
||||
}
|
||||
|
||||
/**
|
||||
* 左边界可以清空的长度
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private int leftBoundEmptyLength() {
|
||||
return leftBoundLength() - 1 - TOP_LINE_SPACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 右边界可以清空的长度
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private int rightBoundEmptyLength() {
|
||||
return rightBoundLength() - 1 - TOP_LINE_SPACE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 让left和right基于this对称
|
||||
*/
|
||||
private void balance(Node left, Node right) {
|
||||
if (left == null || right == null)
|
||||
return;
|
||||
// 【left的尾字符】与【this的首字符】之间的间距
|
||||
int deltaLeft = x - left.rightX();
|
||||
// 【this的尾字符】与【this的首字符】之间的间距
|
||||
int deltaRight = right.x - rightX();
|
||||
|
||||
int delta = Math.max(deltaLeft, deltaRight);
|
||||
int newRightX = rightX() + delta;
|
||||
right.translateX(newRightX - right.x);
|
||||
|
||||
int newLeftX = x - delta - left.width;
|
||||
left.translateX(newLeftX - left.x);
|
||||
}
|
||||
|
||||
private int treeHeight(Node node) {
|
||||
if (node == null) return 0;
|
||||
if (node.treeHeight != 0) return node.treeHeight;
|
||||
node.treeHeight = 1 + Math.max(
|
||||
treeHeight(node.left), treeHeight(node.right));
|
||||
return node.treeHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* 和右节点之间的最小层级距离
|
||||
*/
|
||||
private int minLevelSpaceToRight(Node right) {
|
||||
int thisHeight = treeHeight(this);
|
||||
int rightHeight = treeHeight(right);
|
||||
int minSpace = Integer.MAX_VALUE;
|
||||
for (int i = 0; i < thisHeight && i < rightHeight; i++) {
|
||||
int space = right.levelInfo(i).leftX
|
||||
- this.levelInfo(i).rightX;
|
||||
minSpace = Math.min(minSpace, space);
|
||||
}
|
||||
return minSpace;
|
||||
}
|
||||
|
||||
private LevelInfo levelInfo(int level) {
|
||||
if (level < 0) return null;
|
||||
int levelY = y + level;
|
||||
if (level >= treeHeight(this)) return null;
|
||||
|
||||
List<Node> list = new ArrayList<>();
|
||||
Queue<Node> queue = new LinkedList<>();
|
||||
queue.offer(this);
|
||||
|
||||
// 层序遍历找出第level行的所有节点
|
||||
while (!queue.isEmpty()) {
|
||||
Node node = queue.poll();
|
||||
if (levelY == node.y) {
|
||||
list.add(node);
|
||||
} else if (node.y > levelY) break;
|
||||
|
||||
if (node.left != null) {
|
||||
queue.offer(node.left);
|
||||
}
|
||||
if (node.right != null) {
|
||||
queue.offer(node.right);
|
||||
}
|
||||
}
|
||||
|
||||
Node left = list.get(0);
|
||||
Node right = list.get(list.size() - 1);
|
||||
return new LevelInfo(left, right);
|
||||
}
|
||||
|
||||
/**
|
||||
* 尾字符的下一个位置
|
||||
*/
|
||||
public int rightX() {
|
||||
return x + width;
|
||||
}
|
||||
|
||||
public void translateX(int deltaX) {
|
||||
if (deltaX == 0) return;
|
||||
x += deltaX;
|
||||
|
||||
// 如果是LineNode
|
||||
if (btNode == null) return;
|
||||
|
||||
if (left != null) {
|
||||
left.translateX(deltaX);
|
||||
}
|
||||
if (right != null) {
|
||||
right.translateX(deltaX);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class LevelInfo {
|
||||
int leftX;
|
||||
int rightX;
|
||||
|
||||
public LevelInfo(Node left, Node right) {
|
||||
this.leftX = left.leftBound();
|
||||
this.rightX = right.rightBound();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mj.printer;
|
||||
|
||||
public abstract class Printer {
|
||||
/**
|
||||
* 二叉树的基本信息
|
||||
*/
|
||||
protected BinaryTreeInfo tree;
|
||||
|
||||
public Printer(BinaryTreeInfo tree) {
|
||||
this.tree = tree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成打印的字符串
|
||||
*/
|
||||
public abstract String printString();
|
||||
|
||||
/**
|
||||
* 打印后换行
|
||||
*/
|
||||
public void println() {
|
||||
print();
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
/**
|
||||
* 打印
|
||||
*/
|
||||
public void print() {
|
||||
System.out.print(printString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.mj.printer;
|
||||
|
||||
public class Strings {
|
||||
public static String repeat(String string, int count) {
|
||||
if (string == null) return null;
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
while (count-- > 0) {
|
||||
builder.append(string);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static String blank(int length) {
|
||||
if (length < 0) return null;
|
||||
if (length == 0) return "";
|
||||
return String.format("%" + length + "s", "");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user