static GTreeNode* g_tree_node_restore_left_balance (GTreeNode *node, gint old_balance) { if (!node->left) node->balance += 1; else if ((node->left->balance != old_balance) && (node->left->balance == 0)) node->balance += 1; if (node->balance > 1) return g_tree_node_balance (node); return node; }
static GTreeNode* g_tree_node_restore_right_balance (GTreeNode *node, gint old_balance) { if (!node->right) node->balance -= 1; else if ((node->right->balance != old_balance) && (node->right->balance == 0)) node->balance -= 1; if (node->balance < -1) return g_tree_node_balance (node); return node; }
/* internal remove routine */ static gboolean g_tree_remove_internal (GTree *tree, gconstpointer key, gboolean steal) { GTreeNode *node, *parent, *balance; GTreeNode *path[MAX_GTREE_HEIGHT]; int idx; gboolean left_node; g_return_val_if_fail (tree != NULL, FALSE); if (!tree->root) return FALSE; idx = 0; path[idx++] = NULL; node = tree->root; while (1) { int cmp = tree->key_compare (key, node->key, tree->key_compare_data); if (cmp == 0) break; else if (cmp < 0) { if (!node->left_child) return FALSE; path[idx++] = node; node = node->left; } else { if (!node->right_child) return FALSE; path[idx++] = node; node = node->right; } } /* The following code is almost equal to g_tree_remove_node, * except that we do not have to call g_tree_node_parent. */ balance = parent = path[--idx]; g_assert (!parent || parent->left == node || parent->right == node); left_node = (parent && node == parent->left); if (!node->left_child) { if (!node->right_child) { if (!parent) tree->root = NULL; else if (left_node) { parent->left_child = FALSE; parent->left = node->left; parent->balance += 1; } else { parent->right_child = FALSE; parent->right = node->right; parent->balance -= 1; } } else /* node has a right child */ { GTreeNode *tmp = g_tree_node_next (node); tmp->left = node->left; if (!parent) tree->root = node->right; else if (left_node) { parent->left = node->right; parent->balance += 1; } else { parent->right = node->right; parent->balance -= 1; } } } else /* node has a left child */ { if (!node->right_child) { GTreeNode *tmp = g_tree_node_previous (node); tmp->right = node->right; if (parent == NULL) tree->root = node->left; else if (left_node) { parent->left = node->left; parent->balance += 1; } else { parent->right = node->left; parent->balance -= 1; } } else /* node has a both children (pant, pant!) */ { GTreeNode *prev = node->left; GTreeNode *next = node->right; GTreeNode *nextp = node; int old_idx = idx + 1; idx++; /* path[idx] == parent */ /* find the immediately next node (and its parent) */ while (next->left_child) { path[++idx] = nextp = next; next = next->left; } path[old_idx] = next; balance = path[idx]; /* remove 'next' from the tree */ if (nextp != node) { if (next->right_child) nextp->left = next->right; else nextp->left_child = FALSE; nextp->balance += 1; next->right_child = TRUE; next->right = node->right; } else node->balance -= 1; /* set the prev to point to the right place */ while (prev->right_child) prev = prev->right; prev->right = next; /* prepare 'next' to replace 'node' */ next->left_child = TRUE; next->left = node->left; next->balance = node->balance; if (!parent) tree->root = next; else if (left_node) parent->left = next; else parent->right = next; } } /* restore balance */ if (balance) while (1) { GTreeNode *bparent = path[--idx]; g_assert (!bparent || bparent->left == balance || bparent->right == balance); left_node = (bparent && balance == bparent->left); if(balance->balance < -1 || balance->balance > 1) { balance = g_tree_node_balance (balance); if (!bparent) tree->root = balance; else if (left_node) bparent->left = balance; else bparent->right = balance; } if (balance->balance != 0 || !bparent) break; if (left_node) bparent->balance += 1; else bparent->balance -= 1; balance = bparent; } if (!steal) { if (tree->key_destroy_func) tree->key_destroy_func (node->key); if (tree->value_destroy_func) tree->value_destroy_func (node->value); } g_slice_free (GTreeNode, node); tree->nnodes--; return TRUE; }
/* internal insert routine */ static void g_tree_insert_internal (GTree *tree, gpointer key, gpointer value, gboolean replace) { GTreeNode *node; GTreeNode *path[MAX_GTREE_HEIGHT]; int idx; g_return_if_fail (tree != NULL); if (!tree->root) { tree->root = g_tree_node_new (key, value); tree->nnodes++; return; } idx = 0; path[idx++] = NULL; node = tree->root; while (1) { int cmp = tree->key_compare (key, node->key, tree->key_compare_data); if (cmp == 0) { if (tree->value_destroy_func) tree->value_destroy_func (node->value); node->value = value; if (replace) { if (tree->key_destroy_func) tree->key_destroy_func (node->key); node->key = key; } else { /* free the passed key */ if (tree->key_destroy_func) tree->key_destroy_func (key); } return; } else if (cmp < 0) { if (node->left_child) { path[idx++] = node; node = node->left; } else { GTreeNode *child = g_tree_node_new (key, value); child->left = node->left; child->right = node; node->left = child; node->left_child = TRUE; node->balance -= 1; tree->nnodes++; break; } } else { if (node->right_child) { path[idx++] = node; node = node->right; } else { GTreeNode *child = g_tree_node_new (key, value); child->right = node->right; child->left = node; node->right = child; node->right_child = TRUE; node->balance += 1; tree->nnodes++; break; } } } /* Restore balance. This is the goodness of a non-recursive * implementation, when we are done with balancing we 'break' * the loop and we are done. */ while (1) { GTreeNode *bparent = path[--idx]; gboolean left_node = (bparent && node == bparent->left); g_assert (!bparent || bparent->left == node || bparent->right == node); if (node->balance < -1 || node->balance > 1) { node = g_tree_node_balance (node); if (bparent == NULL) tree->root = node; else if (left_node) bparent->left = node; else bparent->right = node; } if (node->balance == 0 || bparent == NULL) break; if (left_node) bparent->balance -= 1; else bparent->balance += 1; node = bparent; } }
static GTreeNode* g_tree_node_insert (GTree *tree, GTreeNode *node, gpointer key, gpointer value, gboolean replace, gboolean *inserted) { gint old_balance; gint cmp; if (!node) { *inserted = TRUE; return g_tree_node_new (key, value); } cmp = tree->key_compare (key, node->key, tree->key_compare_data); if (cmp == 0) { *inserted = FALSE; if (tree->value_destroy_func) tree->value_destroy_func (node->value); node->value = value; if (replace) { if (tree->key_destroy_func) tree->key_destroy_func (node->key); node->key = key; } else { /* free the passed key */ if (tree->key_destroy_func) tree->key_destroy_func (key); } return node; } if (cmp < 0) { if (node->left) { old_balance = node->left->balance; node->left = g_tree_node_insert (tree, node->left, key, value, replace, inserted); if ((old_balance != node->left->balance) && node->left->balance) node->balance -= 1; } else { *inserted = TRUE; node->left = g_tree_node_new (key, value); node->balance -= 1; } } else if (cmp > 0) { if (node->right) { old_balance = node->right->balance; node->right = g_tree_node_insert (tree, node->right, key, value, replace, inserted); if ((old_balance != node->right->balance) && node->right->balance) node->balance += 1; } else { *inserted = TRUE; node->right = g_tree_node_new (key, value); node->balance += 1; } } if (*inserted) { if ((node->balance < -1) || (node->balance > 1)) node = g_tree_node_balance (node); } return node; }