一棵treap是一棵修改了结点顺序的二叉查找树,如图,显示一个例子,通常树内的每个结点x都有一个关键字值key[x],另外,还要为结点分配priority[x],它是一个独立选取的随机数。
假设所有的优先级是不同的,所有的关键字也是不同的。treap的结点排列成让关键字遵循二叉查找树性质,并且优先级遵循最小堆顺序性质:
1.如果v是u的左孩子,则key[v] < key[u].
2.如果v是u的右孩子,则key[v] > key[u].
3.如果v是u的孩子,则priority[u] > priority[u].
这两个性质的结合就是为什么这种树被称为“treap”的原因,因为它同时具有二叉查找树和堆的特征。
用以下方式考虑treap会有帮助。假设插入关联关键字的结点x1,x2,...,xn到一棵treap内。结果的treap是将这些结点以它们的优先级(随机选取)的顺序插入一棵正常的二叉查找树形成的,亦即priority[xi] < priority[xj]表示xi在xj之前被插入。
在算法导论的12.4节中,其证明了随机构造的二叉查找树的期望高度为O(lgn),因而treap的期望高度亦是O(lgn)。
treap插入操作:
1.按照二叉树的插入方法,将结点插入到树中
2.根据堆的性质(我们这里为最小堆)和优先级的大小调整结点位置。
treap删除操作:
1.找到相应的结点
2.若该结点为叶子结点,则直接删除;
若该结点为只包含一个叶子结点的结点,则将其叶子结点赋值给它;
若该结点为其他情况下的节点,则进行相应的旋转,直到该结点为上述情况之一,然后进行删除。
旋转主要涉及到右旋转的左旋转,下面把右旋转的图画在下面:
代码如下:(已通过GCC和VC编译)
PS:请教一下大家,在C语言中是没有引用的,因而在treap_insert(Node* root, int key, int priority)函数中(第40行),由于root要跟着改变,因而必须传root地址,即&root(第131行),因而导致在写代码时,显得很不好看,如传root的left的地址为参数,必须写成&((*root)->left)(第72行)。如果用C++写,直接用引用,则代码看起来简洁很多,不知在C语言中如何操作?
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 5 typedef struct node_t* Node; 6 typedef struct treap_t* Treap; 7 8 struct node_t 9 { 10 Node left;//左节点 11 Node right;//右节点 12 int priority;//优先级 13 int key;//存储的关键字 14 }; 15 16 struct treap_t 17 { 18 Node root; 19 }; 20 21 //左旋转 22 void rotate_left(Node* node) 23 { 24 Node x = (*node)->right; 25 (*node)->right = x->left; 26 x->left = *node; 27 *node = x; 28 } 29 30 //右旋转 31 void rotate_right(Node* node) 32 { 33 Node x = (*node)->left; 34 (*node)->left = x->right; 35 x->right = *node; 36 *node = x; 37 } 38 39 //插入操作 40 void treap_insert(Node* root, int key, int priority) 41 { 42 //根为NULL,则直接创建此结点为根结点 43 if (*root == NULL) 44 { 45 *root = (Node)malloc(sizeof(struct node_t)); 46 (*root)->left = NULL; 47 (*root)->right = NULL; 48 (*root)->priority = priority; 49 (*root)->key = key; 50 } 51 //向右插入结点 52 else if (key < (*root)->key) 53 { 54 treap_insert(&((*root)->left), key, priority); 55 if ((*root)->left->priority < (*root)->priority) 56 rotate_right(root); 57 } 58 //向左插入结点 59 else 60 { 61 treap_insert(&((*root)->right), key, priority); 62 if ((*root)->right->priority < (*root)->priority) 63 rotate_left(root); 64 } 65 } 66 67 void treap_delete(Node* root, int key) 68 { 69 if (*root != NULL) 70 { 71 if (key < (*root)->key) 72 treap_delete(&((*root)->left), key); 73 else if (key > (*root)->key) 74 treap_delete(&((*root)->right), key); 75 else 76 { 77 //左右孩子都为空不用单独写出来 78 if ((*root)->left == NULL) 79 *root = (*root)->right; 80 else if ((*root)->right == NULL) 81 *root = (*root)->left; 82 else 83 { 84 //先旋转,然后再删除 85 if ((*root)->left->priority < (*root)->right->priority) 86 { 87 rotate_right(root); 88 treap_delete(&((*root)->right), key); 89 } 90 else 91 { 92 rotate_left(root); 93 treap_delete(&((*root)->left), key); 94 } 95 } 96 } 97 } 98 } 99 100 //中序遍历 101 void in_order_traverse(Node root) 102 { 103 if (root != NULL) 104 { 105 in_order_traverse(root->left); 106 printf("%d\t", root->key); 107 in_order_traverse(root->right); 108 } 109 } 110 111 //计算树的高度 112 int depth(Node node) 113 { 114 if(node == NULL) 115 return -1; 116 int l = depth(node->left); 117 int r = depth(node->right); 118 119 return (l < r)?(r+1):(l+1); 120 } 121 122 int main() 123 { 124 Treap treap = (Treap)malloc(sizeof(struct treap_t)); 125 treap->root = NULL; 126 int i = 0; 127 128 srand(time(0)); 129 130 for (i = 0; i < 100; i++) 131 treap_insert(&(treap->root), i, rand()); 132 in_order_traverse(treap->root); 133 printf("\n高度:%d\n", depth(treap->root)); 134 135 printf("---分割线---\n"); 136 137 for (i = 23; i < 59; i++) 138 treap_delete(&(treap->root), i); 139 in_order_traverse(treap->root); 140 printf("\n高度:%d\n", depth(treap->root)); 141 return 0; 142 }