▶ 书中第六章部分程序,包括在加上自己补充的代码,B-树
● B-树
1 package package01; 2 3 import edu.princeton.cs.algs4.StdOut; 4 5 public class class01<Key extends Comparable<Key>, Value> 6 { 7 private static final int M = 4; // 子节点数量为 M-1 8 9 private Node root; // 根节点 10 private int height; // 树高 11 private int n; // 键值对的数量 12 13 private static final class Node // 节点类 14 { 15 private int m; // 当前子节点数量 16 private Entry[] children = new Entry[M]; // 子节点列表 17 18 private Node(int k) // 构造有 k 个子节点的节点 19 { 20 m = k; 21 } 22 } 23 24 private static class Entry // 子节点列表类,内部结点使用 key 和 next,外部节点使用 key 和 value 25 { 26 private Comparable key; 27 private Node next; 28 private final Object val; 29 30 public Entry(Comparable inputKey, Object inputVal, Node inputNext) 31 { 32 key = inputKey; 33 next = inputNext; 34 val = inputVal; 35 } 36 } 37 38 public class01() 39 { 40 root = new Node(0); 41 } 42 43 public int size() 44 { 45 return n; 46 } 47 48 public boolean isEmpty() 49 { 50 return size() == 0; 51 } 52 53 public int height() 54 { 55 return height; 56 } 57 58 public Value get(Key key) 59 { 60 if (key == null) 61 throw new IllegalArgumentException("\n<get> key == null.\n"); 62 return search(root, key, height); 63 } 64 65 private Value search(Node x, Key key, int ht) // 穿如数奉告,每次进入更深层时减一 66 { 67 Entry[] children = x.children; 68 if (ht == 0) // 到达的是叶节点,遍历列表 69 { 70 for (int j = 0; j < x.m; j++) 71 { 72 if (eq(key, children[j].key)) // 找到了,返回 val 73 return (Value)children[j].val; 74 } 75 } 76 else // 到达的是内部节点 77 { 78 for (int j = 0; j < x.m; j++) 79 { 80 if (j == x.m - 1 || less(key, children[j + 1].key)) // j 到达最后或找到合适的子节点,进入下一层 81 return search(children[j].next, key, ht - 1); 82 } 83 } 84 return null; 85 } 86 87 public void put(Key key, Value val) 88 { 89 if (key == null) 90 throw new IllegalArgumentException("\n<put> key == null.\n"); 91 Node u = insert(root, key, val, height); // 执行插入插座并调整总键值对数量 92 n++; 93 if (u == null) // 没有需要调整的节点 94 return; 95 Node t = new Node(2); // 根节点需要分裂,新建一个具有 2 个子节点的节点 96 t.children[0] = new Entry(root.children[0].key, null, root); // 第一个子节点是原来的根节点 97 t.children[1] = new Entry(u.children[0].key, null, u); // 第二个子节点是插入操作导致新增加的节点 98 root = t; // root 编程新建的 t,并增加一层树高 99 height++; 100 } 101 102 private Node insert(Node h, Key key, Value val, int ht) 103 { 104 int j; 105 Entry t = new Entry(key, val, null); 106 if (ht == 0) // 到达的是外部节点 107 { 108 for (j = 0; j < h.m; j++) 109 { 110 if (less(key, h.children[j].key)) // 找到合适的位置就脱出循环 111 break; 112 } 113 } 114 else // 到达的是内部节点 115 { 116 for (j = 0; j < h.m; j++) 117 { 118 if ((j + 1 == h.m) || less(key, h.children[j + 1].key)) // j 到达最后或找到合适的子节点,进入下一层 119 { 120 Node u = insert(h.children[j++].next, key, val, ht - 1);// 在 h.children[j] 进行插入 121 if (u == null) 122 return null; 123 t.key = u.children[0].key; // 把 u 的信息改造到 t 上 124 t.next = u; // 真正 next 是在这里赋值的,指向下一个子节点 125 break; 126 } 127 } 128 } 129 for (int i = h.m; i > j; i--) // 调整本层,排在插入位置之后的元素都向后移动一格 130 h.children[i] = h.children[i - 1]; 131 h.children[j] = t; // 插入节点 t,增加 h 的子节点数量 132 h.m++; 133 return (h.m < M) ? null : split(h); // 节点 h 满了就需要扩容,返回扩容后多出来的节点,用于上一层调整 134 } 135 136 private Node split(Node h) // 分裂节点 h,并返回分裂出来的后一半节点 137 { 138 Node t = new Node(M / 2); 139 h.m = M / 2; // 改 h 的子节点数为一半,相当于废弃后一半记录 140 for (int j = 0; j < M / 2; j++) // 后一半元素搬进 t 141 t.children[j] = h.children[M / 2 + j]; 142 return t; 143 } 144 145 public String toString() 146 { 147 return toStringKernel(root, height, "") + "\n"; 148 } 149 150 private String toStringKernel(Node h, int ht, String indent) // 遍历树转化为字符串 151 { 152 StringBuilder s = new StringBuilder(); 153 Entry[] children = h.children; 154 if (ht == 0) // 叶节点,输出 155 { 156 for (int j = 0; j < h.m; j++) 157 s.append(indent + children[j].key + " " + children[j].val + "\n"); // 注意换行 158 } 159 else // 非叶节点,遍历子节点列表 160 { 161 for (int j = 0; j < h.m; j++) 162 { 163 if (j > 0) 164 s.append(indent + "(" + children[j].key + ")\n"); 165 s.append(toStringKernel(children[j].next, ht - 1, indent + " ")); 166 } 167 } 168 return s.toString(); 169 } 170 171 private boolean less(Comparable k1, Comparable k2) 172 { 173 return k1.compareTo(k2) < 0; 174 } 175 176 private boolean eq(Comparable k1, Comparable k2) 177 { 178 return k1.compareTo(k2) == 0; 179 } 180 181 public static void main(String[] args) // 输入一堆网站和 IP 建立 B-树 182 { 183 class01<String, String> st = new class01<String, String>(); 184 185 st.put("www.cs.princeton.edu", "128.112.136.12"); 186 st.put("www.cs.princeton.edu", "128.112.136.11"); 187 st.put("www.princeton.edu", "128.112.128.15"); 188 st.put("www.yale.edu", "130.132.143.21"); 189 st.put("www.simpsons.com", "209.052.165.60"); 190 st.put("www.apple.com", "17.112.152.32"); 191 st.put("www.amazon.com", "207.171.182.16"); 192 st.put("www.ebay.com", "66.135.192.87"); 193 st.put("www.cnn.com", "64.236.16.20"); 194 st.put("www.google.com", "216.239.41.99"); 195 st.put("www.nytimes.com", "199.239.136.200"); 196 st.put("www.microsoft.com", "207.126.99.140"); 197 st.put("www.dell.com", "143.166.224.230"); 198 st.put("www.slashdot.org", "66.35.250.151"); 199 st.put("www.espn.com", "199.181.135.201"); 200 st.put("www.weather.com", "63.111.66.11"); 201 st.put("www.yahoo.com", "216.109.118.65"); 202 203 StdOut.println("cs.princeton.edu: " + st.get("www.cs.princeton.edu")); 204 StdOut.println("hardvardsucks.com: " + st.get("www.harvardsucks.com")); 205 StdOut.println("simpsons.com: " + st.get("www.simpsons.com")); 206 StdOut.println("apple.com: " + st.get("www.apple.com")); 207 StdOut.println("ebay.com: " + st.get("www.ebay.com")); 208 StdOut.println("dell.com: " + st.get("www.dell.com")); 209 StdOut.println(); 210 211 StdOut.println("size: " + st.size()); 212 StdOut.println("height: " + st.height()); 213 StdOut.println(st); 214 StdOut.println(); 215 } 216 }
● 测试函输出,即生成的树
cs.princeton.edu: 128.112.136.12 hardvardsucks.com: null simpsons.com: 209.052.165.60 apple.com: 17.112.152.32 ebay.com: 66.135.192.87 dell.com: 143.166.224.230 size: 17 height: 2 www.amazon.com 207.171.182.16 www.apple.com 17.112.152.32 www.cnn.com 64.236.16.20 (www.cs.princeton.edu) www.cs.princeton.edu 128.112.136.12 www.cs.princeton.edu 128.112.136.11 www.dell.com 143.166.224.230 (www.ebay.com) www.ebay.com 66.135.192.87 www.espn.com 199.181.135.201 www.google.com 216.239.41.99 (www.microsoft.com) www.microsoft.com 207.126.99.140 www.nytimes.com 199.239.136.200 (www.princeton.edu) www.princeton.edu 128.112.128.15 www.simpsons.com 209.052.165.60 (www.slashdot.org) www.slashdot.org 66.35.250.151 www.weather.com 63.111.66.11 (www.yahoo.com) www.yahoo.com 216.109.118.65 www.yale.edu 130.132.143.21