常见的查找算法(五):树表查找之二 ---- 红黑树
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为 红色 或 黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
- 节点是红色或黑色。
- 根是黑色。
- 所有叶子都是黑色(叶子是NIL节点)。
- 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
下面是一个具体的红黑树的图例:
旋转
旋转是一种能保持二叉搜索树性质的搜索树局部操作。其中两种旋转分别为左旋和右旋:
在某个结点 x 上进行左旋时,假设它的右孩子为y而不是树的 T.nil 结点;x为其右孩子而不是 T.nil 结点的树内任意节点。
左旋以 x 到 y 的链为“支轴”进行,使得 y 成为该子树的新的根节点,x 成为 y 的左孩子,y 的左孩子变成 x 的右孩子;右旋与此相反。
左旋代码:
1 /** 2 * 左旋 3 * 左旋示意图(对节点x进行左旋): 4 * px px 5 * / / 6 * x y 7 * / \ --(左旋)--> / \ 8 * lx y x ry 9 * / \ / \ 10 * ly ry lx ly 11 * 12 * @param x 13 */ 14 private void leftRotate(RBTNode<T> x) { 15 RBTNode<T> y = x.right; // y是x的右节点 16 17 x.right = x.left; // 把x的左节点变为x的右节点 18 // 若y有左子节点,把y的左节点的父节点换成x 19 if (y.left != null) { 20 y.left.parent = x; 21 } 22 23 y.parent = x.parent; // y的父节点(原来是x )设为x的父节点 24 25 // 若x是根节点,y直接变根节点 26 if (x.parent == null) { 27 this.mRoot = y; 28 } else { 29 if (x.parent.left == x) { 30 x.parent.left = y; // 如果x是x父节点的左孩子,把x的父节点的左孩子指向y 31 } else { 32 x.parent.right = y; // 如果x是x父节点的右孩子,把x的父节点的右孩子指向y 33 } 34 } 35 36 y.left = x; // 将y的左节点指向x 37 x.parent = y; // 将x的父节点设为y 38 }
右旋代码:
1 /** 2 * 右旋,操作和左旋相反 3 * 右旋示意图(对节点y进行左旋): 4 * py py 5 * / / 6 * y x 7 * / \ --(右旋)--> / \ 8 * x ry lx y 9 * / \ / \ 10 * lx rx rx ry 11 * 12 * @param y 13 */ 14 private void rightRotate(RBTNode<T> y) { 15 RBTNode<T> x = y.left; // y的左孩子 16 17 y.left = x.right; 18 if (x.right != null) { 19 x.right.parent = y; 20 } 21 22 x.parent = y.parent; 23 24 if (y.parent == null) { 25 this.mRoot = x; 26 } else { 27 if (y.parent.left == y) { 28 y.parent.left = x; 29 } else { 30 y.parent.right = x; 31 } 32 } 33 34 x.right = y; 35 y.parent = x; 36 }
红黑树新结点插入代码:
就像一个普通的二叉搜索树一样,将新结点插入树中,并将其着为红色。之后为了能保证红黑的性质,还需要一个辅助代码对结点重新着色并且旋转。
1 /** 2 * 插入操作 3 * 4 * @param node 5 */ 6 private void insert(RBTNode<T> node) { 7 int result; 8 RBTNode<T> y = null; 9 RBTNode<T> x = this.mRoot; 10 11 // 查找树中插入点的父结点y的位置 12 while (x != null) { 13 y = x; // 注意这里,y不是空的 14 result = node.key.compareTo(x.key); 15 if (result < 0) { 16 x = x.left; 17 } else { 18 x = x.right; 19 } 20 } 21 22 node.parent = y; 23 if (y != null) { 24 result = node.key.compareTo(y.key); 25 if (result < 0) { 26 y.left = node; 27 } else { 28 y.right = node; 29 } 30 } else { 31 this.mRoot = node; 32 } 33 34 node.color = RED; 35 36 // 插入后修正树 37 insertFixUp(node); 38 }
辅助修正函数:
1 /** 2 * 红黑树插入修正函数 3 * 4 * @param node 5 */ 6 private void insertFixUp(RBTNode<T> node) { 7 RBTNode<T> parent, gparent; // 父节点,祖父节点 8 9 while (((parent = parentOf(node)) != null) && isRed(parent)) { 10 gparent = parentOf(parent); 11 12 // 父节点是祖父节点的左孩子 13 if (parent == gparent.left) { 14 RBTNode<T> uncle = gparent.right; // 叔叔节点,祖父的右节点 15 16 // ① 叔叔节点是红色的 17 if ((uncle != null) && isRed(uncle)) { 18 node.setColor(BLACK); 19 parent.setColor(BLACK); 20 gparent.setColor(RED); 21 node = gparent; 22 continue; 23 } 24 25 // ② 叔叔是黑色,且当前节点是右孩子 26 if (parent.right == node) { 27 RBTNode<T> tmp; 28 leftRotate(parent); 29 tmp = parent; 30 parent = node; 31 node = tmp; 32 } 33 34 // ③ 叔叔是黑色,且当前节点是左孩子 35 parent.setColor(BLACK); 36 gparent.setColor(RED); 37 rightRotate(gparent); 38 39 } else { // 父节点是祖父节点的右孩子 40 41 RBTNode<T> uncle = gparent.left; // 叔叔节点,祖父的左节点 42 43 // ① 叔叔节点是红色的 44 if ((uncle != null) && isRed(uncle)) { 45 uncle.setColor(BLACK); 46 parent.setColor(BLACK); 47 gparent.setColor(RED); 48 node = gparent; 49 continue; 50 } 51 52 // ② 叔叔是黑色,且当前节点是左孩子 53 if (parent.left == node) { 54 RBTNode<T> tmp; 55 rightRotate(parent); 56 tmp = parent; 57 parent = node; 58 node = tmp; 59 } 60 61 // ③ 叔叔是黑色,且当前节点是右孩子 62 parent.setColor(BLACK); 63 gparent.setColor(RED); 64 leftRotate(gparent); 65 } 66 } 67 68 this.mRoot.setColor(BLACK); 69 }
修正过程实例:
以下图中的 z 为插入后的结点,y 表示叔结点uncle,图中的每个子树的低端的节点是红黑树代码中的边界,边界中每个节点有黑色的哨兵没有画出来。
下面是介绍的是上面代码中 父节点是祖父节点的左孩子 的代码。
先看图中的第一个树,插入的 z 结点和 z.parent 父节点都是 RED,这违反了性质四。
情况 1(得到的是图中的第二个树):由于图中的第一个树中叔结点是红色,z 结点和 z.parent 父节点都是 RED,结点都要被重新着色,并沿着指针 z 上升;
情况 2(得到的是图中的第三个树):由于图中的第二个树中 z 及其父节点 z.parent 都为红色,其叔结点为黑色,左旋父节点 z.parent后得到;
情况 3(得到的是图中的第四个树):z 是其父节点的左孩子,重新着色后右旋的到图中的第四个树,这样之后就是合法的红黑树了。
分析红黑树的插入时间复杂度:
一颗具有 n 个节点的红黑树高度为O(log n),则按照一个普通的二叉查找树的方式插入结点需要花费 O(log n);修正代码中,当情况 1发生,指针 z沿着树上升2层,才会执行 while 循环,while 循环可能执行的总次数为 O(log n)。所以红黑树的插入的总的时间复杂度为 O(log n)。此外,插入算法中总的来说旋转次数不超过 2 次。
红黑树的删除:
1 /** 2 * 删除树中某个节点 3 * @param node 4 */ 5 private void remove(RBTNode<T> node) { 6 RBTNode<T> child, parent; 7 boolean color; 8 9 if ((node.left != null) && (node.right != null)) { 10 RBTNode<T> replace = node; 11 12 replace = replace.right; 13 while (replace.left != null) { 14 replace = replace.left; 15 } 16 17 if (parentOf(node) != null) { 18 if (parentOf(node).left == node) { 19 parentOf(node).left = replace; 20 } else { 21 parentOf(node).right = replace; 22 } 23 } else { 24 this.mRoot = replace; 25 } 26 27 child = replace.right; 28 parent = parentOf(replace); 29 color = replace.color; 30 31 if (parent == node) { 32 parent = replace; 33 } else { 34 if (child != null) 35 child.setParent(parent); 36 parent.left = child; 37 38 replace.right = node.right; 39 node.right.setParent(replace); 40 } 41 42 replace.parent = node.parent; 43 replace.color = node.color; 44 replace.left = node.left; 45 node.left.parent = replace; 46 47 if (color == BLACK) 48 removeFixup(child, parent); 49 50 node = null; 51 52 return; 53 } 54 55 if (node.left != null) { 56 child = node.left; 57 } else { 58 child = node.right; 59 } 60 61 parent = node.parent; 62 color = node.color; 63 64 if (child != null) 65 child.parent = parent; 66 67 if (parent != null) { 68 if (parent.left == node) { 69 parent.left = child; 70 } else { 71 parent.right = child; 72 } 73 } else { 74 this.mRoot = child; 75 } 76 77 if (color == BLACK) 78 removeFixup(child, parent); 79 node = null; 80 }
删除修正函数:
1 /** 2 * 删除修正函数 3 * @param x 4 * @param parent 5 */ 6 private void removeFixup(RBTNode<T> x, RBTNode<T> parent) { 7 RBTNode<T> w; 8 9 while ((x == null || isBlack(x)) && (x != this.mRoot)) { 10 if (parent.left == x) { 11 w = parent.right; 12 13 // ① x的兄弟结点w是红色的 14 if (isRed(w)) { 15 w.setColor(BLACK); // w染黑 16 parent.setColor(RED); 17 leftRotate(parent); 18 w = parent.right; 19 } 20 21 // ② x的兄弟结点w是黑色的,而且w的两个孩子结点都是黑色的 22 if ((w.left == null || isBlack(w.left)) && 23 (w.right == null || isBlack(w.right))) { 24 w.setColor(RED); 25 x = parent; 26 parent = parentOf(x); 27 28 } else { 29 30 // ③ x的兄弟结点w是黑色的,w的右孩子是黑色的,w的左孩子是红色的 31 if ((w.right == null || isBlack(w.right)) && 32 (w.left == null || isBlack(w.left))) { 33 w.left.setColor(BLACK); 34 w.setColor(RED); 35 rightRotate(w); 36 w = parent.right; 37 } 38 39 // ④ x的兄弟结点w是黑色的,w的右孩子是红色的 40 w.setColor(parent.color); 41 parent.setColor(BLACK); 42 w.right.setColor(BLACK); 43 leftRotate(parent); 44 x = this.mRoot; 45 46 break; 47 } 48 49 } else { 50 51 w = parent.left; 52 // ① x的兄弟结点w是红色的 53 if (isRed(w)) { 54 w.setColor(BLACK); 55 parent.setColor(RED); 56 rightRotate(parent); 57 w = parent.left; 58 } 59 60 // ② x的兄弟结点w是黑色的,而且w的两个孩子结点都是黑色的 61 if ((w.left == null || isBlack(w.left)) && 62 (w.right == null || isBlack(w.right))) { 63 w.setColor(RED); 64 x = parent; 65 parent = parentOf(x); 66 } else { 67 68 // ③ x的兄弟结点w是黑色的,w的右孩子是黑色的,w的左孩子是红色的 69 if (w.left == null || isBlack(w.left)) { 70 w.right.setColor(BLACK); 71 w.setColor(RED); 72 leftRotate(w); 73 w = parent.left; 74 } 75 76 // ④ x的兄弟结点w是黑色的,w的右孩子是红色的 77 w.setColor(parent.color); 78 parent.setColor(BLACK); 79 w.left.setColor(BLACK); 80 rightRotate(parent); 81 x = this.mRoot; 82 83 break; 84 } 85 } 86 } 87 88 if (x != null) 89 x.setColor(BLACK); 90 }
红黑树总的代码(包含测试代码):
1 package tree; 2 3 /** 4 * @program: MyPractice 5 * @description: 红黑树 6 * @author: Mr.Wu 7 * @create: 2019-08-28 17:19 8 **/ 9 public class RBTree<T extends Comparable<T>> { 10 // 根节点 11 private RBTNode<T> mRoot; 12 13 private static final boolean RED = false; 14 private static final boolean BLACK = true; 15 16 private static final int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80}; 17 private static final boolean mDebugInsert = true; // "插入"动作的检测开关(false,关闭;true,打开) 18 19 /** 20 * 左旋 21 * 左旋示意图(对节点x进行左旋): 22 * px px 23 * / / 24 * x y 25 * / \ --(左旋)--> / \ 26 * lx y x ry 27 * / \ / \ 28 * ly ry lx ly 29 * 30 * @param x 31 */ 32 private void leftRotate(RBTNode<T> x) { 33 RBTNode<T> y = x.right; // y是x的右节点 34 35 x.right = x.left; // 把x的左节点变为x的右节点 36 // 若y有左子节点,把y的左节点的父节点换成x 37 if (y.left != null) { 38 y.left.parent = x; 39 } 40 41 y.parent = x.parent; // y的父节点(原来是x )设为x的父节点 42 43 // 若x是根节点,y直接变根节点 44 if (x.parent == null) { 45 this.mRoot = y; 46 } else { 47 if (x.parent.left == x) { 48 x.parent.left = y; // 如果x是x父节点的左孩子,把x的父节点的左孩子指向y 49 } else { 50 x.parent.right = y; // 如果x是x父节点的右孩子,把x的父节点的右孩子指向y 51 } 52 } 53 54 y.left = x; // 将y的左节点指向x 55 x.parent = y; // 将x的父节点设为y 56 } 57 58 /** 59 * 右旋,操作和左旋相反 60 * 右旋示意图(对节点y进行左旋): 61 * py py 62 * / / 63 * y x 64 * / \ --(右旋)--> / \ 65 * x ry lx y 66 * / \ / \ 67 * lx rx rx ry 68 * 69 * @param y 70 */ 71 private void rightRotate(RBTNode<T> y) { 72 RBTNode<T> x = y.left; // y的左孩子 73 74 y.left = x.right; 75 if (x.right != null) { 76 x.right.parent = y; 77 } 78 79 x.parent = y.parent; 80 81 if (y.parent == null) { 82 this.mRoot = x; 83 } else { 84 if (y.parent.left == y) { 85 y.parent.left = x; 86 } else { 87 y.parent.right = x; 88 } 89 } 90 91 x.right = y; 92 y.parent = x; 93 } 94 95 /** 96 * 新建节点 97 * 98 * @param key 99 */ 100 public void insert(T key) { 101 RBTNode<T> node = new RBTNode<>(BLACK, key, null, null, null); 102 if (node != null) { 103 insert(node); 104 } 105 } 106 107 /** 108 * 插入操作 109 * 110 * @param node 111 */ 112 private void insert(RBTNode<T> node) { 113 int result; 114 RBTNode<T> y = null; 115 RBTNode<T> x = this.mRoot; 116 117 // 查找树中插入点的父结点y的位置 118 while (x != null) { 119 y = x; // 注意这里,y不是空的 120 result = node.key.compareTo(x.key); 121 if (result < 0) { 122 x = x.left; 123 } else { 124 x = x.right; 125 } 126 } 127 128 node.parent = y; 129 if (y != null) { 130 result = node.key.compareTo(y.key); 131 if (result < 0) { 132 y.left = node; 133 } else { 134 y.right = node; 135 } 136 } else { 137 this.mRoot = node; 138 } 139 140 node.color = RED; 141 142 // 插入后修正树 143 insertFixUp(node); 144 } 145 146 /** 147 * 红黑树插入修正函数 148 * 149 * @param node 150 */ 151 private void insertFixUp(RBTNode<T> node) { 152 RBTNode<T> parent, gparent; // 父节点 与 祖父节点 153 154 while (((parent = parentOf(node)) != null) && isRed(parent)) { 155 gparent = parentOf(parent); 156 157 // 父节点是祖父节点的左孩子 158 if (parent == gparent.left) { 159 RBTNode<T> uncle = gparent.right; // 叔叔节点,祖父的右节点 160 161 // ① 叔叔节点是红色的 162 if ((uncle != null) && isRed(uncle)) { 163 node.setColor(BLACK); 164 parent.setColor(BLACK); 165 gparent.setColor(RED); 166 node = gparent; 167 continue; 168 } 169 170 // ② 叔叔是黑色,且当前节点是右孩子 171 if (parent.right == node) { 172 RBTNode<T> tmp; 173 leftRotate(parent); 174 tmp = parent; 175 parent = node; 176 node = tmp; 177 } 178 179 // ③ 叔叔是黑色,且当前节点是左孩子 180 parent.setColor(BLACK); 181 gparent.setColor(RED); 182 rightRotate(gparent); 183 184 } else { // 父节点是祖父节点的右孩子 185 186 RBTNode<T> uncle = gparent.left; // 叔叔节点,祖父的左节点 187 188 // ① 叔叔节点是红色的 189 if ((uncle != null) && isRed(uncle)) { 190 uncle.setColor(BLACK); 191 parent.setColor(BLACK); 192 gparent.setColor(RED); 193 node = gparent; 194 continue; 195 } 196 197 // ② 叔叔是黑色,且当前节点是左孩子 198 if (parent.left == node) { 199 RBTNode<T> tmp; 200 rightRotate(parent); 201 tmp = parent; 202 parent = node; 203 node = tmp; 204 } 205 206 // ③ 叔叔是黑色,且当前节点是右孩子 207 parent.setColor(BLACK); 208 gparent.setColor(RED); 209 leftRotate(gparent); 210 } 211 } 212 213 this.mRoot.setColor(BLACK); 214 } 215 216 private boolean isRed(RBTNode<T> node) { 217 return (node != null) && (node.color == RED); 218 } 219 220 private boolean isBlack(RBTNode<T> node) { 221 return !isRed(node); 222 } 223 224 private RBTNode<T> parentOf(RBTNode<T> node) { 225 return (node != null) ? node.parent : null; 226 } 227 228 public void clear() { 229 destroy(mRoot); 230 mRoot = null; 231 } 232 233 private void destroy(RBTNode<T> tree) { 234 if (tree == null) { 235 return; 236 } 237 if (tree.left != null) { 238 destroy(tree.left); 239 } 240 if (tree.right != null) { 241 destroy(tree.right); 242 } 243 244 tree = null; 245 } 246 247 public void preOrder() { 248 preOrder(mRoot); 249 } 250 251 private void preOrder(RBTNode<T> tree) { 252 if (tree != null) { 253 System.out.print(tree.key + ""); 254 preOrder(tree.left); 255 preOrder(tree.right); 256 } 257 } 258 259 public void print() { 260 if (mRoot != null) { 261 print(mRoot, mRoot.key, 0); 262 } 263 } 264 265 private void print(RBTNode<T> tree, T key, int direction) { 266 if (tree != null) { 267 if (direction == 0) { 268 System.out.printf("%2d(B) is root\n", tree.key); 269 } else { 270 System.out.printf("%2d(%s) is %2d's %6s child\n", tree.key, isRed(tree) ? "R" : "B", 271 key, direction == 1 ? "right" : "left"); 272 } 273 print(tree.left, tree.key, -1); 274 print(tree.right, tree.key, 1); 275 } 276 } 277 278 public static void main(String[] args) { 279 int i, ilen = a.length; 280 RBTree<Integer> tree = new RBTree<>(); 281 282 System.out.printf("== 原始数据: "); 283 for (i = 0; i < ilen; i++) 284 System.out.printf("%d ", a[i]); 285 System.out.printf("\n"); 286 287 for (i = 0; i < ilen; i++) { 288 tree.insert(a[i]); 289 // 设置mDebugInsert=true,测试"添加函数" 290 if (mDebugInsert) { 291 System.out.printf("== 添加节点: %d\n", a[i]); 292 System.out.printf("== 树的详细信息: \n"); 293 tree.print(); 294 System.out.printf("\n"); 295 } 296 } 297 298 System.out.printf("== 前序遍历: "); 299 tree.preOrder(); 300 301 // System.out.printf("\n== 中序遍历: "); 302 // tree.inOrder(); 303 // 304 // System.out.printf("\n== 后序遍历: "); 305 // tree.postOrder(); 306 System.out.printf("\n"); 307 308 System.out.printf("== 最小值: %s\n", tree.minimum()); 309 System.out.printf("== 最大值: %s\n", tree.maximum()); 310 System.out.printf("== 树的详细信息: \n"); 311 tree.print(); 312 System.out.printf("\n"); 313 314 // 销毁二叉树 315 tree.clear(); 316 } 317 318 private RBTNode<T> maximum(RBTNode<T> tree) { 319 if (tree == null) 320 return null; 321 while (tree.right != null) 322 tree = tree.right; 323 return null; 324 } 325 326 public T maximum() { 327 RBTNode<T> p = maximum(mRoot); 328 if (p != null) 329 return p.key; 330 return null; 331 } 332 333 private RBTNode<T> minimum(RBTNode<T> tree) { 334 if (tree == null) 335 return null; 336 while (tree.left != null) 337 tree = tree.left; 338 return tree; 339 } 340 341 public T minimum() { 342 RBTNode<T> p = minimum(mRoot); 343 if (p != null) 344 return p.key; 345 return null; 346 } 347 348 public class RBTNode<T extends Comparable<T>> { 349 boolean color; // 颜色 350 T key; // 关键字 351 RBTNode<T> left; 352 RBTNode<T> right; 353 RBTNode<T> parent; 354 355 public RBTNode(boolean color, T key, RBTNode<T> left, RBTNode<T> right, RBTNode<T> parent) { 356 this.color = color; 357 this.key = key; 358 this.left = left; 359 this.right = right; 360 this.parent = parent; 361 } 362 363 public boolean isColor() { 364 return color; 365 } 366 367 public void setColor(boolean color) { 368 this.color = color; 369 } 370 371 public T getKey() { 372 return key; 373 } 374 375 public void setKey(T key) { 376 this.key = key; 377 } 378 379 public RBTNode<T> getLeft() { 380 return left; 381 } 382 383 public void setLeft(RBTNode<T> left) { 384 this.left = left; 385 } 386 387 public RBTNode<T> getRight() { 388 return right; 389 } 390 391 public void setRight(RBTNode<T> right) { 392 this.right = right; 393 } 394 395 public RBTNode<T> getParent() { 396 return parent; 397 } 398 399 public void setParent(RBTNode<T> parent) { 400 this.parent = parent; 401 } 402 } 403 }
(未完待续)
原文地址:https://www.cnblogs.com/magic-sea/p/11390892.html
- 十分钟带你了解服务化框架
- 十分钟带你了解服务化框架
- WCF技术剖析之十七:消息(Message)详解(上篇)
- 微信年底重磅更新,这次小程序才是重头戏!
- 《EnterLib PIAB深入剖析》系列博文汇总
- Nodejs学习笔记(八)--- Node.js + Express 实现上传文件功能(felixge/node-formidable)
- 大牛教你使用7种卷积神经网络进行物体检测!
- Enterprise Library深入解析与灵活应用(3):倘若将Unity、PIAB、Exception Handling引入MVP模式.. .. ..
- 别对我说谎!你的小九九我都知道
- Spring集成RabbitMQ-使用RabbitMQ更方便
- Nodejs学习笔记(三)--- 模块
- 使用JClouds在Java中获取和发布云服务器
- Silverlight单元测试框架
- Enterprise Library深入解析与灵活应用(2): 通过SqlDependency实现Cache和Database的同步
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 设计之禅——备忘录模式
- 设计之禅——访问者模式
- 在scite编辑器中使用astyle格式化c代码
- 简单的51单片机多任务操作系统(C51)
- 使用KEIL C51实现的简单合作式多任务操作系统内核
- Zookeeper——分布式一致性协议及Zookeeper Leader选举原理
- 使用KEIL C51实现的简单合作式多任务操作系统内核(单片机实现版本)
- Zookeeper——基本使用以及应用场景(手写实现分布式锁和rpc框架)
- 函数指针数组指针+结构体数组
- Zookeeper——Watcher原理详解
- 51多任务系统,可以运行
- notepad++中设置python运行
- Dubbo——SPI及自适应扩展原理
- Dubbo——服务发布原理
- Dubbo——服务引用