forked from sduonline/sc-resources
		
	增加淘宝群内容,修改部分文件组织
This commit is contained in:
		| @@ -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