红黑树是一种自平衡二叉查找树(binary search tree,BST),红黑树是一种比较复杂的数据结构,红黑树查找、插入、删除元素的时间复杂度为O(log n),n是树中元素的数目.文章的要讲的知识点如下:

  一、红黑树的基本介绍

    红黑树插入节点

    红黑树删除节点

  二、红黑树应用实例:Jdk中的TreeMap

一、红黑树的基本介绍

   二叉查找树(binary search tree,BST,也称排序二叉树)虽然可以快速检索,但在最坏的情况下:如果插入的节点集本身就是有序的,要么是由小到大排列,要么是由大到小排列,那么最后得到的二叉查找树将变成链表:所有节点只有左节点(如果插入节点集本身是大到小排列);或所有节点只有右节点(如果插入节点集本身是小到大排列)。在这种情况下,二叉查找树就变成了普通链表,其检索效率就会下降为O(n)。

  为了改变二叉查找树存在的不足,Rudolf Bayer 与 1972 年发明了另一种改进后的二叉查找树:红黑树。红黑树是一颗自平衡二叉查找树,它有以下特点:

  1. 节点是红色或黑色
  2. 根节点是黑色
  3. 所有空节点(NIL节点)都是黑色
  4. 如果一个节点是红色的,则它的子节点都是黑的.(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  5. 对每个节点来说,从它到他的所有子孙叶子节点的路径上的黑色节点数目相同(节点的黑色高度相同)

红黑树实例:

        

  红黑树的性质确保了红黑树的关键特性: 从根到叶子的最长的可能路径不多于最短的可能路径的两倍长。这就保证了红黑树大致上是平衡的。因为操作比如插入、删除和查找某个值的最坏情况时间都要求与树的高度成比例,这个在高度上的理论上限允许红黑树在最坏情况下都是高效的,而不同于普通的二叉查找树

简而言之:二叉搜索树只限定了节点的大小顺序,红黑树不只限定了节点的大小,还对节点颜色做了限制,并用此来保证红黑树的平衡性.红黑树中插入有序序列并不会像二叉搜索树那样变为链表结构,如下图:

              

  上面我们已经说明了红黑树的概念以及其特性,接下来我们分析红黑树的常见操作:搜索节点、插入节点、删除节点.红黑树是一颗自平衡二叉树,对红黑树操作需要继续保持红黑树的特性。红黑树的搜索操作和排序二叉树一样,不一样的地方在于插入和删除节点可能会破坏红黑树的性质,所以在插入和删除节点后需要维护红黑树的性质.1,2,3条性质比较简单,维护4,5性质比较复杂,实际上红黑树的平衡也就是通过性质4,5来维护的.

  红黑树插入节点和删除节点的操作比较复杂,如果对红黑树不太熟悉,可以先进行实际操作下,这里提供一个很形象的红黑树动画教程:http://www.bbniu.com/matrix/ShowApplication.aspx?id=149.(要安装Silverlight),遇到难以理解的地方,可以对照着操作,多操作几次,有助于理解.

  介绍插入和删除操作前,先介绍维护红黑树性质的两种方式:节点变色和旋转.

    1.变色:实际就是通过改变节点的颜色来维护红黑树性质

    

    2.旋转:改变父子节点的位置

    

    这两个操作不了解没关系,后面用到的时候自然就懂了.

红黑树插入节点

  首先,按照二叉查找树(BST)的方式插入新的节点.然后修复二叉树,保持红黑树的性质.

  insert(N):

    1. 按照BST插入方式插入新节点

  不同的地方:

    1. N节点的两个子节点都指向NIL节点(空节点,黑色)
    2. N节点为红色
    3. 处理不符合红黑树性质的部分

  怎么去处理呢?首先我们分析下插入操作会破坏哪些性质.

  1. 每个节点不是黑就是红
  2. root是黑色的
  3. 每个空节点(nil节点)都是黑色的
  4. 如果一个节点是红色的,则它的子节点都是黑色的
  5. 对每个节点来说,从它到它的所有子孙叶子节点的路径上含有一样数目的黑色节点.

因为新增的节点N是红色的,N节点取代一个nil节点,而N的两个子节点都是nil节点.所以新增节点N不会破坏性质5;

如果破坏性质2,说明新增节点就是root节点,整棵树只有N节点,此时把N节点变为黑色节点就可以.很容易处理;

如果破坏性质4,说明N节点的父节点P节点是一个红色节点.

为了便于分析问题,对G,P,U,N节点进行约定:

N节点:新增节点

P节点:N节点的父节点

U节点:N节点的叔父节点

G节点:P节点的父节点,N节点的爷爷节点.

根据约定,G,P,U,N节点存在如下关系图:

            

  

我们针对破坏性质4的情况进行分析.也就是P节点为红节点,G节点为黑节点,新增节点N为红节点的情况.此时,有如下三种情形:

  ① U节点为红色节点

  ② U节点为黑色节点,N节点为P节点的右子节点

  ③ U节点为黑色节点,N节点为P节点的左子节点

我们依次对上面三种情形进行分析:

case 1: U节点为红色节点,N为P的左子节点或者右子节点

      

针对case1我们的处理方式是:将P,U节点变成黑色,G节点变成红色.此时,需要继续对G节点进行分析:

  • 如果G节点的父节点为黑色节点,则ok.
  • 如果G节点的父节点为红节点,同样破坏了性质4,这时我们把G节点当成新增节点,进行递归处理.

case 2:U节点为黑色节点或nil节点,N节点为P节点的右子节点

      

case2最终会转换成case3.

case 3:U节点为黑色节点或nil节点,N节点为P节点的左子节点.

    

上面就是红黑树插入节点后的修复操作.如果理解起来不太顺畅,请对着情形多操作几次动画教程.实际操作能加深理解!

红黑树插入性能分析:

  插入时间:原本正常的二叉搜索树要花O(log n)的时间,因为树的高度最高为2log(n+1)。

花费在调整上的时间:在最槽糕的状况下,case1一直重复发生,也就是N节点每次都需要往上移两层,执行时间与树的高度成正比,也是O(log n).在case3的情况下,只需要一次调整就能满足红黑树的性质,也就是O(1).另外,在case2情况下,最多只需要调整两次(不会发生第二次case2)

红黑树删除节点

按照二叉查找树删除节点的方式删除节点,然后修复红黑树,保持红黑树的性质.

delete(z)

删除节点z首先要找到z节点.接下来有三种情形:

  • 如果z是叶子节点,直接删除
  • 如果z节点有一个孩子节点,将z节点唯一的子节点放到z节点的位置.
  • 如果z节点有两个子节点,找左子树中最大的节点(或者右子树中最小的节点)x,用x节点的值替换z节点的值,x节点的颜色改变成z的颜色,然后删除x节点.

现在我们分析,删除节点在什么情形下会破坏红黑树的规则。

当要删除的节点z为红色节点时:

  1. 因为z节点是红色节点,black height不会改变--->性质5不会破坏
  2. 会不会造成连续两个红色节点?--->性质4

    如果z是被删掉的,因为z是红色节点,所有z的上下层都是黑色节点,所以不会产生连续的红色节点

  3. z如果是红色,则z节点不可能为root节点,所以也不会破坏性质2

so,只有当z为黑色节点才会破坏红黑树的性质,需要在删除节点后修复操作.

当z节点是黑色节点,可能违反规则的有如下情况:

  1. z是root节点,z删除以后,它的红色子节点变成了root节点
  2. z原本上下两层都是红色节点,删除以后造成两个红色节点相邻
  3. z删掉后,造成black height不一致(因为z是黑色节点)

我们假设z节点为删除节点,x节点为z节点的子孙节点,x节点的兄弟节点为w,会有以下四种情形:

  case 1: w是红色节点

  case 2: w是黑色节点,w的孩子都是黑色的

  case 3: w是黑色节点,w的左孩子是红色的,w的右孩子是黑色节点

  case 4: w是黑色节点,w的右孩子是红色的

下面,我们依次分析这四种情形.

  case 1: w是红色节点

    

    ps:+1表示该节点需要一个黑色节点来维持红黑树的性质.case1最终会转换到下面的三种情形之一.

  case 2:w是黑色节点,w的所有儿子都是黑色的.

      

  

    这种情况下,递归对x进行删除处理以保持红黑树的性质.

  case 3: w是黑色的,w的左孩子是红色的,右孩子是黑色的.

      

  case 4: w是黑色节点,w的右孩子是红色的.

        

删除算法性能分析:

查找节点的时间复杂度:O(log n).

  修复红黑树需要的时间:

最槽糕的情形是case2一直重复发生,需要O(log n)

case 1,case3,case4都是O(1).最多需要三次旋转.

红黑树实例: Java TreeMap

  前面讲了红黑树,现在我们来看一个红黑树应用实例: jdk中基于红黑树实现的TreeMap.

在TreeMap中,每一个Entry代表一个节点,TreeMap就是用红黑树存储Entry的集合.下面是Entry的定义: 

  1. static final class Entry<K,V> implements Map.Entry<K,V> {
  2. K key;
  3. V value;
  4. // 左子节点的引用
  5. Entry<K,V> left = null;
  6. // 右子节点的引用
  7. Entry<K,V> right = null;
  8. // 父节点引用
  9. Entry<K,V> parent;
  10. // 节点颜色,默认为黑色
  11. boolean color = BLACK;
  12. Entry(K key, V value, Entry<K,V> parent) {
  13. this.key = key;
  14. this.value = value;
  15. this.parent = parent;
  16. }
  17. public K getKey() {
  18. return key;
  19. }
  20. public V getValue() {
  21. return value;
  22. }
  23. public V setValue(V value) {
  24. V oldValue = this.value;
  25. this.value = value;
  26. return oldValue;
  27. }
  28. public boolean equals(Object o) {
  29. if (!(o instanceof Map.Entry))
  30. return false;
  31. Map.Entry<?,?> e = (Map.Entry<?,?>)o;
  32. return valEquals(key,e.getKey()) && valEquals(value,e.getValue());
  33. }
  34. public int hashCode() {
  35. int keyHash = (key==null ? 0 : key.hashCode());
  36. int valueHash = (value==null ? 0 : value.hashCode());
  37. return keyHash ^ valueHash;
  38. }
  39. public String toString() {
  40. return key + "=" + value;
  41. }
  42. }

TreeMap搜索节点

  1. public V get(Object key) {
  2. Entry<K,V> p = getEntry(key);
  3. return (p==null ? null : p.value);
  4. }
  5. final Entry<K,V> getEntry(Object key) {
  6. //comparator!=null说明程序采用定制排序,调用getEntryUsingComparator()处理
  7. if (comparator != null)
  8. return getEntryUsingComparator(key);
  9. if (key == null)
  10. throw new NullPointerException();
  11. // 将key强转为Comparable实例
  12. Comparable<? super K> k = (Comparable<? super K>) key;
  13. // 从root节点开始查找
  14. Entry<K,V> p = root;
  15. while (p != null) {
  16. // key与当前节点的key进行比较
  17. int cmp = k.compareTo(p.key);
  18. // 如果key小于当前节点的key,去”左子树”搜索
  19. if (cmp < 0)
  20. p = p.left;
  21. // 如果key大于当前节点的key,去”右子树”搜索
  22. else if (cmp > 0)
  23. p = p.right;
  24. else // 相等则返回当前节点
  25. return p;
  26. }
  27. return null;
  28. }

  上面的getEntry(Object key)方法就是利用排序二叉树的特征来搜索目标Entry,程序从root节点开始,如果key大于root.key,则向右子树搜索,如果小于,就向左子树搜索,如果相等,则返回当前节点.

getEntryUsingComparator()方法与getEntry()方法的思路完全类似,前者采用的是定制排序,后者采用的是Entry.key的自然排序(此时,key对象必须实现Comparable接口,重写compareTo方法).

TreeMap插入节点

  插入节点思路:

  1. 确定节点的插入位置

  2.查找新增节点的parent

  3. 如果新增节点比parent大,则做为parent的右子节点,如果较小,则做为parent的左子节点,如果相等,则替换

  4.检查是否破坏红黑树性质,如果破坏,修复.未破坏--->ok.

我们看TreeMap中的put(k,v):

  1. public V put(K key, V value) {
  2. Entry<K,V> t = root;
  3. if (t == null) {
  4. // 空树,以Entry做为root节点创建红黑树
  5. root = new Entry<K,V>(key, value, null);
  6. size = 1;
  7. modCount++;
  8. return null;
  9. }
  10. int cmp;
  11. Entry<K,V> parent;
  12. Comparator<? super K> cpr = comparator;
  13. // 获取插入节点的parent节点,与搜索节点类似
  14. if (cpr != null) {// 如果采用定制排序
  15. do {
  16. parent = t;
  17. cmp = cpr.compare(key, t.key);
  18. if (cmp < 0)
  19. t = t.left;
  20. else if (cmp > 0)
  21. t = t.right;
  22. else
  23. return t.setValue(value);
  24. } while (t != null);
  25. }
  26. else {
  27. if (key == null)
  28. throw new NullPointerException();
  29. Comparable<? super K> k = (Comparable<? super K>) key;
  30. do {
  31. parent = t;
  32. cmp = k.compareTo(t.key);
  33. if (cmp < 0)
  34. t = t.left;
  35. else if (cmp > 0)
  36. t = t.right;
  37. else
  38. return t.setValue(value);
  39. } while (t != null);
  40. }
  41. // 创建新节点,父节点为parent
  42. Entry<K,V> e = new Entry<K,V>(key, value, parent);
  43. if (cmp < 0)//如果新节点比parent小,则左子节点
  44. parent.left = e;
  45. else
  46. parent.right = e;
  47. // 修复红黑树
  48. fixAfterInsertion(e);
  49. size++;
  50. modCount++;
  51. return null;
  52. }

上面就是红黑树的插入过程,我们接下来看TreeMap是如何修复红黑树的.

  1. private void fixAfterInsertion(Entry<K,V> x) {
  2. // 新增节点为红色
  3. x.color = RED;
  4. // 如果x节点不为空,也不是root节点,并且x的父节点不是红色节点
  5. while (x != null && x != root && x.parent.color == RED) {
  6. // x的父节点是x的爷爷节点的左子节点
  7. if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
  8. // 获取x节点的叔父节点
  9. Entry<K,V> y = rightOf(parentOf(parentOf(x)));
  10. // 叔父节点为红色,对应红黑树修复中的case1
  11. if (colorOf(y) == RED) {
  12. //把x的父节点,叔父节点变成黑色,x的爷爷节点变成红色,x的爷爷节点变成红色后,可能造成两个红色节点连续,所以需要对x的爷爷节点进行递归处理
  13. setColor(parentOf(x), BLACK);
  14. setColor(y, BLACK);
  15. setColor(parentOf(parentOf(x)), RED);
  16. x = parentOf(parentOf(x));
  17. } else {//叔父节点为黑色,case2、3
  18. //x为右子节点,对应case2
  19. if (x == rightOf(parentOf(x))) {
  20. //x指向x的父节点, 对x的父节点进行左旋转,改成rotateLeft(parentOf(x))更容易理解
  21. x = parentOf(x);
  22. rotateLeft(x);
  23. }
  24. // case3,x为左子节点,x节点的父节点变成黑色,爷爷节点变成红色,然后对爷爷节点进行右旋转
  25. setColor(parentOf(x), BLACK);
  26. setColor(parentOf(parentOf(x)), RED);
  27. rotateRight(parentOf(parentOf(x)));
  28. }
  29. } else {//x的父节点是x爷爷节点的右子节点,思路与上面一样
  30. Entry<K,V> y = leftOf(parentOf(parentOf(x)));
  31. if (colorOf(y) == RED) {
  32. setColor(parentOf(x), BLACK);
  33. setColor(y, BLACK);
  34. setColor(parentOf(parentOf(x)), RED);
  35. x = parentOf(parentOf(x));
  36. } else {
  37. if (x == leftOf(parentOf(x))) {
  38. x = parentOf(x);
  39. rotateRight(x);
  40. }
  41. setColor(parentOf(x), BLACK);
  42. setColor(parentOf(parentOf(x)), RED);
  43. rotateLeft(parentOf(parentOf(x)));
  44. }
  45. }
  46. }
  47. // root节点为黑色
  48. root.color = BLACK;
  49. }

不能理解的地方可以回头再看下红黑树插入的那一段.修复方法和红黑树插入那一节的case1,case2,case3都是对应的.

TreeMap删除节点

删除节点的思路:

  1. 首先,使用排序二叉树中删除节点的方法来删除节点
  2. 检查是否破坏红黑树性质,如果有破坏,进行修复
  1. private void deleteEntry(Entry<K,V> p) {
  2. modCount++;
  3. size--;
  4. //如果节点有左右子节点
  5. if (p.left != null && p.right != null) {
  6. //使用p节点的中序后继节点(p左子树中的最大节点)代替p节点
  7. Entry<K,V> s = successor (p);
  8. p.key = s.key;
  9. p.value = s.value;
  10. p = s;
  11. }
  12. // replacement代表p的子节点
  13. Entry<K,V> replacement = (p.left != null ? p.left : p.right);
  14. // 如果p有子节点
  15. if (replacement != null) {
  16. //replacement替换父节点p的位置,根据p节点在p节点的位置,决定replace在p节点父节点的位置
  17. replacement.parent = p.parent;
  18. //如果p没有父节点,replacement为root节点
  19. if (p.parent == null)
  20. root = replacement;
  21. else if (p == p.parent.left)
  22. // 如果p是其父节点的左子节点,replacement就成为p节点的父节点的左子节点
  23. p.parent.left = replacement;
  24. else
  25. //replacement成为p节点的父节点的右子节点
  26. p.parent.right = replacement;
  27. //删除p节点
  28. p.left = p.right = p.parent = null;
  29. // 如果删除的是黑色节点,需要修复红黑树
  30. if (p.color == BLACK)
  31. fixAfterDeletion(replacement);
  32. } else if (p.parent == null) {
  33. //如果p节点就是root节点,直接删除
  34. root = null;
  35. } else { // 如果p节点为叶子节点
  36. // p节点为黑色节点,删除黑色节点可能破坏红黑树性质,需要修复
  37. if (p.color == BLACK)
  38. fixAfterDeletion(p);
  39. // 删除节点
  40. if (p.parent != null) {
  41. if (p == p.parent.left)
  42. p.parent.left = null;
  43. else if (p == p.parent.right)
  44. p.parent.right = null;
  45. p.parent = null;
  46. }
  47. }
  48. }

上面就是红黑树删除节点的过程,只有当删除或移动的节点是黑色节点的时候,才可能会破坏红黑树的性质,此时需要修复红黑树,下面我们看红黑树删除节点以后的修复方法.

  1. private void fixAfterDeletion(Entry<K,V> x) {
  2. // x非root节点,且为黑色节点
  3. while (x != root && colorOf(x) == BLACK) {
  4. if (x == leftOf(parentOf(x))) {// x为左子节点
  5. // 获取x的兄弟节点
  6. Entry<K,V> sib = rightOf(parentOf(x));
  7. // 如果x的兄弟节点为红色,对应红黑树删除的case1
  8. if (colorOf(sib) == RED) {
  9. //兄弟节点变成黑色,父节点变成红色,左旋转父节点
  10. setColor(sib, BLACK);
  11. setColor(parentOf(x), RED);
  12. rotateLeft(parentOf(x));
  13. sib = rightOf(parentOf(x));
  14. }
  15. // case2:兄弟节点的子节点都是黑色,设置兄弟节点为红色,递归处理
  16. if (colorOf(leftOf(sib)) == BLACK &&
  17. colorOf(rightOf(sib)) == BLACK) {
  18. setColor(sib, RED);
  19. x = parentOf(x);
  20. } else {
  21. // case3:转成case4处理
  22. if (colorOf(rightOf(sib)) == BLACK) {
  23. setColor(leftOf(sib), BLACK);
  24. setColor(sib, RED);
  25. rotateRight(sib);
  26. sib = rightOf(parentOf(x));
  27. }
  28. // case4:兄弟节点变成与父节点一样的颜色
  29. setColor(sib, colorOf(parentOf(x)));
  30. //设置父节点,兄弟节点的右子节点为黑色,x的父节点左旋转
  31. setColor(parentOf(x), BLACK);
  32. setColor(rightOf(sib), BLACK);
  33. rotateLeft(parentOf(x));
  34. x = root;//终止循环
  35. }
  36. } else { // x为右子节点,与上面的处理思路一样
  37. Entry<K,V> sib = leftOf(parentOf(x));
  38.  
  39. if (colorOf(sib) == RED) {
  40. setColor(sib, BLACK);
  41. setColor(parentOf(x), RED);
  42. rotateRight(parentOf(x));
  43. sib = leftOf(parentOf(x));
  44. }
  45.  
  46. if (colorOf(rightOf(sib)) == BLACK &&
  47. colorOf(leftOf(sib)) == BLACK) {
  48. setColor(sib, RED);
  49. x = parentOf(x);
  50. } else {
  51. if (colorOf(leftOf(sib)) == BLACK) {
  52. setColor(rightOf(sib), BLACK);
  53. setColor(sib, RED);
  54. rotateLeft(sib);
  55. sib = leftOf(parentOf(x));
  56. }
  57. setColor(sib, colorOf(parentOf(x)));
  58. setColor(parentOf(x), BLACK);
  59. setColor(leftOf(sib), BLACK);
  60. rotateRight(parentOf(x));
  61. x = root;
  62. }
  63. }
  64. }
  65. // 设置root节点为黑色
  66. setColor(x, BLACK);
  67. }

红黑树的删除节点修复算法比较复杂,可以参照前面红黑树删除那一节的内容来理解.

红黑树及其实例JDK中的TreeMap的更多相关文章

  1. 关于红黑树,在HashMap中是怎么应用的?

    关于红黑树,在HashMap中是怎么应用的? 前言 在阅读HashMap源码时,会发现在HashMap中使用了红黑树,所以需要先了解什么是红黑树,以及其原理.从而再进一步阅读HashMap中的链表到红 ...

  2. 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现

    本文转载自http://www.ibm.com/developerworks/cn/java/j-lo-tree/ 目录: TreeSet 和 TreeMap 的关系 TreeMap 的添加节点 Tr ...

  3. java中treemap和treeset实现(红黑树)

    java中treemap和treeset实现(红黑树)   TreeMap 的实现就是红黑树数据结构,也就说是一棵自平衡的排序二叉树,这样就可以保证当需要快速检索指定节点. TreeSet 和 Tre ...

  4. 通过分析 JDK 源代码研究 TreeMap 红黑树算法实现--转

    TreeMap 和 TreeSet 是 Java Collection Framework 的两个重要成员,其中 TreeMap 是 Map 接口的常用实现类,而 TreeSet 是 Set 接口的常 ...

  5. 通过分析 JDK 源代码研究 TreeMap 红黑树算法实

    TreeMap和TreeSet是Java Collection Framework的两个重要成员,其中TreeMap是Map接口的常用实现类,而TreeSet是Set接口的常用实现类.虽然HashMa ...

  6. 红黑树之 原理和算法详细介绍(阿里面试-treemap使用了红黑树) 红黑树的时间复杂度是O(lgn) 高度<=2log(n+1)1、X节点左旋-将X右边的子节点变成 父节点 2、X节点右旋-将X左边的子节点变成父节点

    红黑树插入删除 具体参考:红黑树原理以及插入.删除算法 附图例说明   (阿里的高德一直追着问) 或者插入的情况参考:红黑树原理以及插入.删除算法 附图例说明 红黑树与AVL树 红黑树 的时间复杂度 ...

  7. 数据结构与算法(十):红黑树与TreeMap详细解析

    本文目录 一.为什么要创建红黑树这种数据结构 在上篇我们了解了AVL树,既然已经有了AVL这种平衡的二叉排序树,为什么还要有红黑树呢? AVL树通过定义我们知道要求树中每一个结点的左右子树高度差的绝对 ...

  8. TreeMap(红黑树)源码分析

    1. HashMap.Entry(红黑树节点) private static final boolean RED = false; private static final boolean BLACK ...

  9. (转)为什么HashMap中链表长度超过8会转换成红黑树

    原博地址:https://blog.csdn.net/xingfei_work/article/details/79637878 HashMap在jdk1.8之后引入了红黑树的概念,表示若桶中链表元素 ...

随机推荐

  1. 使用Repeater控件实现三层嵌套以及分页效果

    PS: 第一次用Repeater控件 记录一下 请忽略我的命名不规范  请忽略我的最终效果图(太丑了) 需要用到的朋友可以自行调整的漂亮点 ====================最终效果图===== ...

  2. Linux系统修改Mysql密码

    一.拥有原来的myql的root的密码: 方法一: 在mysql系统外,使用mysqladmin mysqladmin -u root -p password "test123" ...

  3. java 获取控制台输入

    读取控制台输入 从控制台读取一行数据,返回值字符串 public class IO { public static void main(String args[]) throws IOExceptio ...

  4. oracle逐步学习总结之oracle分页查询(基础三)

    原创作品,转载请在文章开头明显位置注明出处:https://www.cnblogs.com/sunshine5683/p/10087205.html oracle 的分页有三种,下面将这三种方式一一列 ...

  5. hdu 1401

    Solitaire Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total S ...

  6. MS SQL Server数据库在线管理工具

    MS SQL Server数据库以其优异的性能,被广泛使用,特别是政务,医疗行业.但是远程维护挺不方便的,目前有一款基于WEB的工具TreeSoft数据库管理系统. 免安装,直接解压就可以用了.直接通 ...

  7. 为什么说 LINQ 要胜过 SQL

    如果你还没有沉溺于 LINQ,就会想这有啥大惊小怪的.SQL 并没有坏掉,为什么还要对它进行修补呢? 为什么我们还需要另外一种查询语言呢? 流行的说法是 LINQ 同 C#(或者 VB)集成在了一起, ...

  8. 获取本地内网和外网IP地址

    public class IPUtil { /// <summary> /// 获取本地内网IP /// </summary> /// <returns></ ...

  9. Mysql-安装指南

    1.设置用户名密码 首次登录后修改密码如下: 如果密码设置太过简单会报以下错误 mysql修改密码Your password does not satisfy the current policy r ...

  10. CSS通过设置position定位的三种常用定位

    CSS中可以通过设置为元素设置一个position属性值,从而达到将不同的元素显示在不同的位置,或者固定显示在某一个位置,或者显示在某一层页面之上. position的值可以设为relative,ab ...