fork 自 sduonline/sc-resources
增加淘宝群内容,修改部分文件组织
此提交包含在:
@@ -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>
|
@@ -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
|
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
未顯示二進位檔案。
@@ -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", "");
|
||||
}
|
||||
}
|
新增問題並參考
封鎖使用者