新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t

  • Author:Echo Chen(陈斌)

  • Email:chenb19870707@gmail.com

  • Blog:Blog.csdn.net/chen19870707

  • Date:October 27h, 2014

    1.ngx_rbtree优势和特点

    ngx_rbtree是一种使用红黑树实现的关联容器。关于红黑树的特性,在《手把手实现红黑树》已经具体介绍,这里就仅仅探讨ngx_rbtree与众不同的地方;ngx_rbtree红黑树容器中的元素都是有序的,支持高速索引,插入,删除操作,也支持范围查询,遍历操作。应用很广泛。

    2.源码位置

    头文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_rbtree.h

    源文件:http://trac.nginx.org/nginx/browser/nginx/src/core/ngx_rbtree.c

    3.数据结构定义

    能够看到ngx_rbtree的结点ngx_rbtree_node_t结构跟一般的红黑树差点儿相同,都是由键值key、左孩子left、右孩子right、父亲结点parent、颜色值color,不同的是ngx_rbtree_node_t这里多了一个data。但依据官方文档记在,因为data仅仅有一个字节,表示太少,非常少使用到

    1. 1: typedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;
    1. 2: 
    1. 3: struct ngx_rbtree_node_s {
    1. 4:     ngx_rbtree_key_t       key;
    1. 5:     ngx_rbtree_node_t     *left;
    1. 6:     ngx_rbtree_node_t     *right;
    1. 7:     ngx_rbtree_node_t     *parent;
    1. 8:     u_char                 color;
    1. 9:     u_char                 data;
    1. 10: };

    ngx_rbtree_t的结构也与一般红黑树同样,右root结点和哨兵叶子结点(sentinel)组成,不同的是这里多了一个 函数指针inserter。它决定了在加入结点是新加还是替换。

    1. 1: typedef struct ngx_rbtree_s  ngx_rbtree_t;
    1. 2: 
    1. 3: typedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,
    1. 4:     ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
    1. 5: 
    1. 6: struct ngx_rbtree_s {
    1. 7:     ngx_rbtree_node_t     *root;
    1. 8:     ngx_rbtree_node_t     *sentinel;
    1. 9:     ngx_rbtree_insert_pt   insert;
    1. 10: };

    4.ngx_rbtree初始化 ngx_rbtree_init

    当中tree为ngx_rbtree_t类型,即为红黑树。s为ngx_rbtree_node_t,是rbtree的根节点,i即为上节提到的决定插入是新结点还是替换的函数指针。首先将根节点涂成 黑色(红黑树基本性质)。然后把 红黑树的 根节点和 哨兵结点 都指向这个结点。

    1. 1: #define ngx_rbtree_init(tree, s, i)                                           \
    1. 2:     ngx_rbtree_sentinel_init(s);                                              \
    1. 3:     (tree)->root = s;                                                         \
    1. 4:     (tree)->sentinel = s;                                                     \
    1. 5:     (tree)->insert = i
    1. 6: 
    1. 7: #define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)

    5.ngx_rbtree 左旋 ngx_rbtree_left_rotate 和 右旋 ngx_rbtree_right_rotate

    能够看到,经典代码总是永恒的,ngx_rbtree的左旋右旋也是參考《算法导论》导论中的步骤和伪代码。对比我自己的实现的《手把手实现红黑树》,与我自己实现的左旋右旋代码基本一致。我图解了具体的过程,有不清楚的能够參考《手把手实现红黑树》。

    1. 1: static ngx_inline void
    1. 2: ngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
    1. 3:     ngx_rbtree_node_t *node)
    1. 4: {
    1. 5:     ngx_rbtree_node_t  *temp;
    1. 6: 
    1. 7:     temp = node->right;
    1. 8:     node->right = temp->left;
    1. 9: 
    1. 10:     if (temp->left != sentinel) {
    1. 11:         temp->left->parent = node;
    1. 12:     }
    1. 13: 
    1. 14:     temp->parent = node->parent;
    1. 15: 
    1. 16:     if (node == *root) {
    1. 17:         *root = temp;
    1. 18: 
    1. 19:     } else if (node == node->parent->left) {
    1. 20:         node->parent->left = temp;
    1. 21: 
    1. 22:     } else {
    1. 23:         node->parent->right = temp;
    1. 24:     }
    1. 25: 
    1. 26:     temp->left = node;
    1. 27:     node->parent = temp;
    1. 28: }
    1. 1: static ngx_inline void
    1. 2: ngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,
    1. 3:     ngx_rbtree_node_t *node)
    1. 4: {
    1. 5:     ngx_rbtree_node_t  *temp;
    1. 6: 
    1. 7:     temp = node->left;
    1. 8:     node->left = temp->right;
    1. 9: 
    1. 10:     if (temp->right != sentinel) {
    1. 11:         temp->right->parent = node;
    1. 12:     }
    1. 13: 
    1. 14:     temp->parent = node->parent;
    1. 15: 
    1. 16:     if (node == *root) {
    1. 17:         *root = temp;
    1. 18: 
    1. 19:     } else if (node == node->parent->right) {
    1. 20:         node->parent->right = temp;
    1. 21: 
    1. 22:     } else {
    1. 23:         node->parent->left = temp;
    1. 24:     }
    1. 25: 
    1. 26:     temp->right = node;
    1. 27:     node->parent = temp;
    1. 28: }

    6.ngx_rbtree插入 ngx_rbtree_insert

    ngx_rbtree_insert也是分为两步,插入和调整。因为这两项都在《手把手实现红黑树》中做了详解,这里就不在啰嗦。这里值得一提的是,还记得node_rbtree_t
    结构中的insert指针吗?这里就是通过这个函数指针来实现的插入。

    一个小小的技巧就实现了多态。而且它给出了 唯一值和时间类型的key 插入方法。能够满足一般需求,用户也能够实现自己的插入方法。

    1. void
    2. ngx_rbtree_insert(ngx_thread_volatile ngx_rbtree_t *tree,
    3. ngx_rbtree_node_t *node)
    4. {
    5. ngx_rbtree_node_t **root, *temp, *sentinel;
    6.  
    7. /* a binary tree insert */
    8.  
    9. root = (ngx_rbtree_node_t **) &tree->root;
    10. sentinel = tree->sentinel;
    11.  
    12. if (*root == sentinel) {
    13. node->parent = NULL;
    14. node->left = sentinel;
    15. node->right = sentinel;
    16. ngx_rbt_black(node);
    17. *root = node;
    18.  
    19. return;
    20. }
    21.  
    22. tree->insert(*root, node, sentinel);
    23.  
    24. /* re-balance tree */
    25.  
    26. while (node != *root && ngx_rbt_is_red(node->parent)) {
    27.  
    28. if (node->parent == node->parent->parent->left) {
    29. temp = node->parent->parent->right;
    30.  
    31. if (ngx_rbt_is_red(temp)) {
    32. ngx_rbt_black(node->parent);
    33. ngx_rbt_black(temp);
    34. ngx_rbt_red(node->parent->parent);
    35. node = node->parent->parent;
    36.  
    37. } else {
    38. if (node == node->parent->right) {
    39. node = node->parent;
    40. ngx_rbtree_left_rotate(root, sentinel, node);
    41. }
    42.  
    43. ngx_rbt_black(node->parent);
    44. ngx_rbt_red(node->parent->parent);
    45. ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);
    46. }
    47.  
    48. } else {
    49. temp = node->parent->parent->left;
    50.  
    51. if (ngx_rbt_is_red(temp)) {
    52. ngx_rbt_black(node->parent);
    53. ngx_rbt_black(temp);
    54. ngx_rbt_red(node->parent->parent);
    55. node = node->parent->parent;
    56.  
    57. } else {
    58. if (node == node->parent->left) {
    59. node = node->parent;
    60. ngx_rbtree_right_rotate(root, sentinel, node);
    61. }
    62.  
    63. ngx_rbt_black(node->parent);
    64. ngx_rbt_red(node->parent->parent);
    65. ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);
    66. }
    67. }
    68. }
    69.  
    70. ngx_rbt_black(*root);
    71. }

    6.1 唯一值类型插入

    这个即为一般红黑树的插入方法,循环,假设插入的值比当前节点小,就进入左子树,否则进入右子树。直至遇到叶子结点。叶子节点就是要链入红黑树的位置。

    1. 1: void
    1. 2: ngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
    1. 3:     ngx_rbtree_node_t *sentinel)
    1. 4: {
    1. 5:     ngx_rbtree_node_t  **p;
    1. 6: 
    1. 7:     for ( ;; ) {
    1. 8: 
    1. 9:         p = (node->key < temp->key) ? &temp->left : &temp->right;
    1. 10: 
    1. 11:         if (*p == sentinel) {
    1. 12:             break;
    1. 13:         }
    1. 14: 
    1. 15:         temp = *p;
    1. 16:     }
    1. 17: 
    1. 18:     *p = node;
    1. 19:     node->parent = temp;
    1. 20:     node->left = sentinel;
    1. 21:     node->right = sentinel;
    1. 22:     ngx_rbt_red(node);
    1. 23: }

    假设有相等的结点。会直接被覆盖,如上图插入key为2的结点,则当tmp 为2的结点时。p为叶子遍历结束。这样p就会被覆盖为新的值。

    6.2 唯一时间类型插入

    唯一差别就是推断大小时,採用了两个值相减,避免溢出。

    1. 1: typedef ngx_int_t   ngx_rbtree_key_int_t;
    1. 2: void
    1. 3: ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
    1. 4:     ngx_rbtree_node_t *sentinel)
    1. 5: {
    1. 6:     ngx_rbtree_node_t  **p;
    1. 7: 
    1. 8:     for ( ;; ) {
    1. 9: 
    1. 10:         /*
    1. 11:          * Timer values
    1. 12:          * 1) are spread in small range, usually several minutes,
    1. 13:          * 2) and overflow each 49 days, if milliseconds are stored in 32 bits.
    1. 14:          * The comparison takes into account that overflow.
    1. 15:          */
    1. 16: 
    1. 17:         /*  node->key < temp->key */
    1. 18: 
    1. 19:        p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0)
    1. 20:             ? &temp->left : &temp->right;
    1. 21: 
    1. 22:         if (*p == sentinel) {
    1. 23:             break;
    1. 24:         }
    1. 25: 
    1. 26:         temp = *p;
    1. 27:     }
    1. 28: 
    1. 29:     *p = node;
    1. 30:     node->parent = temp;
    1. 31:     node->left = sentinel;
    1. 32:     node->right = sentinel;
    1. 33:     ngx_rbt_red(node);
    1. 34: }

    7.ngx_rbtree删除ngx_rbtree_delete

    也是依照《算法导论》上的步骤,先删除后调整,在《手把手实现红黑树》已介绍,请參考

    1. 1: void
    1. 2: ngx_rbtree_delete_delete(ngx_thread_volatile ngx_rbtree_t *tree,
    1. 3:     ngx_rbtree_node_t *node)
    1. 4: {
    1. 5:     ngx_uint_t           red;
    1. 6:     ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;
    1. 7: 
    1. 8:     /* a binary tree delete */
    1. 9: 
    1. 10:     root = (ngx_rbtree_node_t **) &tree->root;
    1. 11:     sentinel = tree->sentinel;
    1. 12: 
    1. 13:     if (node->left == sentinel) {
    1. 14:         temp = node->right;
    1. 15:         subst = node;
    1. 16: 
    1. 17:     } else if (node->right == sentinel) {
    1. 18:         temp = node->left;
    1. 19:         subst = node;
    1. 20: 
    1. 21:     } else {
    1. 22:         subst = ngx_rbtree_min(node->right, sentinel);
    1. 23: 
    1. 24:         if (subst->left != sentinel) {
    1. 25:             temp = subst->left;
    1. 26:         } else {
    1. 27:             temp = subst->right;
    1. 28:         }
    1. 29:     }
    1. 30: 
    1. 31:     if (subst == *root) {
    1. 32:         *root = temp;
    1. 33:         ngx_rbt_black(temp);
    1. 34: 
    1. 35:         /* DEBUG stuff */
    1. 36:         node->left = NULL;
    1. 37:         node->right = NULL;
    1. 38:         node->parent = NULL;
    1. 39:         node->key = 0;
    1. 40: 
    1. 41:         return;
    1. 42:     }
    1. 43: 
    1. 44:     red = ngx_rbt_is_red(subst);
    1. 45: 
    1. 46:     if (subst == subst->parent->left) {
    1. 47:         subst->parent->left = temp;
    1. 48: 
    1. 49:     } else {
    1. 50:         subst->parent->right = temp;
    1. 51:     }
    1. 52: 
    1. 53:     if (subst == node) {
    1. 54: 
    1. 55:         temp->parent = subst->parent;
    1. 56: 
    1. 57:     } else {
    1. 58: 
    1. 59:         if (subst->parent == node) {
    1. 60:             temp->parent = subst;
    1. 61: 
    1. 62:         } else {
    1. 63:             temp->parent = subst->parent;
    1. 64:         }
    1. 65: 
    1. 66:         subst->left = node->left;
    1. 67:         subst->right = node->right;
    1. 68:         subst->parent = node->parent;
    1. 69:         ngx_rbt_copy_color(subst, node);
    1. 70: 
    1. 71:         if (node == *root) {
    1. 72:             *root = subst;
    1. 73: 
    1. 74:         } else {
    1. 75:             if (node == node->parent->left) {
    1. 76:                 node->parent->left = subst;
    1. 77:             } else {
    1. 78:                 node->parent->right = subst;
    1. 79:             }
    1. 80:         }
    1. 81: 
    1. 82:         if (subst->left != sentinel) {
    1. 83:             subst->left->parent = subst;
    1. 84:         }
    1. 85: 
    1. 86:         if (subst->right != sentinel) {
    1. 87:             subst->right->parent = subst;
    1. 88:         }
    1. 89:     }
    1. 90: 
    1. 91:     /* DEBUG stuff */
    1. 92:     node->left = NULL;
    1. 93:     node->right = NULL;
    1. 94:     node->parent = NULL;
    1. 95:     node->key = 0;
    1. 96: 
    1. 97:     if (red) {
    1. 98:         return;
    1. 99:     }
    1. 100: 
    1. 101:     /* a delete fixup */
    1. 102: 
    1. 103:     while (temp != *root && ngx_rbt_is_black(temp)) {
    1. 104: 
    1. 105:         if (temp == temp->parent->left) {
    1. 106:             w = temp->parent->right;
    1. 107: 
    1. 108:             if (ngx_rbt_is_red(w)) {
    1. 109:                 ngx_rbt_black(w);
    1. 110:                 ngx_rbt_red(temp->parent);
    1. 111:                 ngx_rbtree_left_rotate(root, sentinel, temp->parent);
    1. 112:                 w = temp->parent->right;
    1. 113:             }
    1. 114: 
    1. 115:             if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
    1. 116:                 ngx_rbt_red(w);
    1. 117:                 temp = temp->parent;
    1. 118: 
    1. 119:             } else {
    1. 120:                 if (ngx_rbt_is_black(w->right)) {
    1. 121:                     ngx_rbt_black(w->left);
    1. 122:                     ngx_rbt_red(w);
    1. 123:                     ngx_rbtree_right_rotate(root, sentinel, w);
    1. 124:                     w = temp->parent->right;
    1. 125:                 }
    1. 126: 
    1. 127:                 ngx_rbt_copy_color(w, temp->parent);
    1. 128:                 ngx_rbt_black(temp->parent);
    1. 129:                 ngx_rbt_black(w->right);
    1. 130:                 ngx_rbtree_left_rotate(root, sentinel, temp->parent);
    1. 131:                 temp = *root;
    1. 132:             }
    1. 133: 
    1. 134:         } else {
    1. 135:             w = temp->parent->left;
    1. 136: 
    1. 137:             if (ngx_rbt_is_red(w)) {
    1. 138:                 ngx_rbt_black(w);
    1. 139:                 ngx_rbt_red(temp->parent);
    1. 140:                 ngx_rbtree_right_rotate(root, sentinel, temp->parent);
    1. 141:                 w = temp->parent->left;
    1. 142:             }
    1. 143: 
    1. 144:             if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {
    1. 145:                 ngx_rbt_red(w);
    1. 146:                 temp = temp->parent;
    1. 147: 
    1. 148:             } else {
    1. 149:                 if (ngx_rbt_is_black(w->left)) {
    1. 150:                     ngx_rbt_black(w->right);
    1. 151:                     ngx_rbt_red(w);
    1. 152:                     ngx_rbtree_left_rotate(root, sentinel, w);
    1. 153:                     w = temp->parent->left;
    1. 154:                 }
    1. 155: 
    1. 156:                 ngx_rbt_copy_color(w, temp->parent);
    1. 157:                 ngx_rbt_black(temp->parent);
    1. 158:                 ngx_rbt_black(w->left);
    1. 159:                 ngx_rbtree_right_rotate(root, sentinel, temp->parent);
    1. 160:                 temp = *root;
    1. 161:             }
    1. 162:         }
    1. 163:     }
    1. 164: 
    1. 165:     ngx_rbt_black(temp);
    1. 166: }

    8.实战

    因为ngx_rbtree_t未牵涉到内存池,所以很easy抽出来使用,例如以下为实现了插入、打印最小值、删除的样例

    1. 1: #include <iostream>
    1. 2: #include <algorithm>
    1. 3: #include <pthread.h>
    1. 4: #include <time.h>
    1. 5: #include <stdio.h>
    1. 6: #include <errno.h>
    1. 7: #include <string.h>
    1. 8: #include "ngx_queue.h"
    1. 9: #include "ngx_rbtree.h"
    1. 10: 
    1. 11: 
    1. 12: int main()
    1. 13: {
    1. 14: 
    1. 15:     ngx_rbtree_t tree;
    1. 16:     ngx_rbtree_node_t sentinel;
    1. 17: 
    1. 18:     ngx_rbtree_init(&tree,&sentinel,ngx_rbtree_insert_value);
    1. 19: 
    1. 20:     ngx_rbtree_node_t *rbnode = new ngx_rbtree_node_t[100];
    1. 21:     for(int i = 99; i >= 0 ;i--)
    1. 22:     {
    1. 23:         rbnode[i].key = i;
    1. 24:         rbnode[i].parent = NULL;
    1. 25:         rbnode[i].left = NULL;
    1. 26:         rbnode[i].right = NULL;
    1. 27:         ngx_rbtree_insert(&tree,&rbnode[i]);
    1. 28:     }
    1. 29: 
    1. 30:     for(int i = 0; i < 100;i++)
    1. 31:     {
    1. 32:          ngx_rbtree_node_t *p = ngx_rbtree_min(tree.root,&sentinel);
    1. 33:          std::cout << p->key << "  ";
    1. 34:          ngx_rbtree_delete(&tree,p);
    1. 35:      }
    1. 36: 
    1. 37: 
    1. 38:     delete[] rbnode;
    1. 39: 
    1. 40:     return 0;
    1. 41: }

    执行结果:

    -

    Echo Chen:Blog.csdn.net/chen19870707

    -

  • 版权声明:本文博客原创文章,博客,未经同意,不得转载。

    新秀nginx源代码分析数据结构篇(四)红黑树ngx_rbtree_t的更多相关文章

    1. 新秀nginx源代码分析数据结构篇(两) 双链表ngx_queue_t

      nginx源代码分析数据结构篇(两) 双链表ngx_queue_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn. ...

    2. 菜鸟nginx源代码剖析数据结构篇(一)动态数组ngx_array_t

      菜鸟nginx源代码剖析数据结构篇(一)动态数组ngx_array_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csd ...

    3. 菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t

      菜鸟nginx源代码剖析数据结构篇(九) 内存池ngx_pool_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

    4. 菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表ngx_chain_t

      菜鸟nginx源代码剖析数据结构篇(八) 缓冲区链表 ngx_chain_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog. ...

    5. 菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock

      菜鸟nginx源代码剖析数据结构篇(十) 自旋锁ngx_spinlock Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.cs ...

    6. 菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上)

      菜鸟nginx源代码剖析数据结构篇(六) 哈希表 ngx_hash_t(上) Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog. ...

    7. 菜鸟nginx源代码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)

        菜鸟nginx源代码剖析数据结构篇(七) 哈希表 ngx_hash_t(下)   Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:B ...

    8. 菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t[转]

      菜鸟nginx源码剖析数据结构篇(四)红黑树ngx_rbtree_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...

    9. nginx源代码分析--进程间通信机制 &amp; 同步机制

      Nginx源代码分析-进程间通信机制 从nginx的进程模型能够知道.master进程和worker进程须要通信,nginx中通信的方式有套接字.共享内存.信号.对于master进程,从外部接受信号, ...

    随机推荐

    1. mysql-定时调用存储过程

      mysql定时调用存储过程,对表数据集表结构进行备份 存储过程实例: BEGIN DECLARE tname varchar(64); set @tname = CONCAT('RENAME TABL ...

    2. 小米2S TWRP 3.0.2-0 最新版Recovery

      主界面 使用了我最新修改的内核 下载地址: 链接: http://pan.baidu.com/s/1i5xwddb 密码: 7dyb 验证信息: md5sum: dca410f33020eb87986 ...

    3. 做web项目时对代码修改后浏览器端不生效的应对方法(持续更新)

      做web项目时,经常会遇到修改了代码,但浏览器端没有生效,原因是多种多样的,我会根据我遇到的情况逐步更新解决办法 1.运行的时候采用debug模式,一般情况下使用项目部署按钮右边那个按钮下的tomca ...

    4. Sliverlight之 矢量绘图

      目标:在两天内完成一个环形图的绘制 准备:第5章 矢量绘图 1,形状绘图(见Project11) (1)线条用什么标签表示,它有哪几个重要属性 说明: Line标签 x1 y1表示起始点x,y坐标 x ...

    5. 在汉澳sinox2014建立ZFS高可靠文件存储系统

      在汉澳sinox2014建立ZFS高可靠文件存储系统 汉澳sinox2014能够用比較小的固态硬盘安装,文件系统能够用zfs系统存放. 请准备一些硬盘,比方三块SCSI硬盘:da0,da1,da2 如 ...

    6. 国内三大PTPrivate Tracker站分析

      除这一行外,下面全部内容都是转载.出处不明. 国内三大PT(Private Tracker)站分析 先郑重的声明一下:本文以下的内容所有是复制粘贴的,不代表老夫的观点. 事实上内容我也没细致看. 贴这 ...

    7. 使用 Cordova+Visual Studio 创建跨平台移动应用(1)

      1简介 本章节是关于Visual Studio Tools for Apache Cordova的,目前此产品只发布了预览版.Visual Studio for Apache Cordova帮助熟悉V ...

    8. android 他们定义对话框

      创建一个布局文件 my_dialog.xml <?xml version="1.0" encoding="utf-8"?> <Relative ...

    9. 【C++探索之旅】第一部分第二课:C++编程的必要软件

      内容简介 1.第一部分第二课:C++编程的必要软件 2.第一部分第三课预告:第一个C++程序 C++编程的必要软件 经过上一课之后,大家是不是摩拳擦掌,准备大干一场了呢. 这一课我们来做一些C++开发 ...

    10. canvas绘制百分比圆环进度条

      开发项目,PM会跟踪项目进度:完成某个事情,也可以设置一个完成的进度. 这里用canvas绘制一个简单百分比圆环进度条. 看下效果: 1. 动画方式   2. 静默方式   // 贴上代码,仅供参考 ...