📄 dict.c
字号:
/* * Dictionary Abstract Data Type * Copyright (C) 1997 Kaz Kylheku <kaz@ashi.footprints.net> * * Free Software License: * * All rights are reserved by the author, with the following exceptions: * Permission is granted to freely reproduce and distribute this software, * possibly in exchange for a fee, provided that this copyright notice appears * intact. Permission is also granted to adapt this software to produce * derivative works, as long as the modified versions carry this copyright * notice and additional notices stating that the work has been modified. * This source code may be translated into executable form and incorporated * into proprietary software; there is no requirement for such software to * contain a copyright notice related to this source. * * $Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $ * $Name: kazlib_1_20 $ */#include <stdlib.h>#include <stddef.h>#include <assert.h>#define DICT_IMPLEMENTATION#include "dict.h"#ifdef KAZLIB_RCSIDstatic const char rcsid[] = "$Id: dict.c,v 1.40.2.7 2000/11/13 01:36:44 kaz Exp $";#endif/* * These macros provide short convenient names for structure members, * which are embellished with dict_ prefixes so that they are * properly confined to the documented namespace. It's legal for a * program which uses dict to define, for instance, a macro called ``parent''. * Such a macro would interfere with the dnode_t struct definition. * In general, highly portable and reusable C modules which expose their * structures need to confine structure member names to well-defined spaces. * The resulting identifiers aren't necessarily convenient to use, nor * readable, in the implementation, however! */#define left dict_left#define right dict_right#define parent dict_parent#define color dict_color#define key dict_key#define data dict_data#define nilnode dict_nilnode#define nodecount dict_nodecount#define maxcount dict_maxcount#define compare dict_compare#define allocnode dict_allocnode#define freenode dict_freenode#define context dict_context#define dupes dict_dupes#define dictptr dict_dictptr#define dict_root(D) ((D)->nilnode.left)#define dict_nil(D) (&(D)->nilnode)#define DICT_DEPTH_MAX 64static dnode_t *dnode_alloc(void *context);static void dnode_free(dnode_t *node, void *context);/* * Perform a ``left rotation'' adjustment on the tree. The given node P and * its right child C are rearranged so that the P instead becomes the left * child of C. The left subtree of C is inherited as the new right subtree * for P. The ordering of the keys within the tree is thus preserved. */static void rotate_left(dnode_t *upper){ dnode_t *lower, *lowleft, *upparent; lower = upper->right; upper->right = lowleft = lower->left; lowleft->parent = upper; lower->parent = upparent = upper->parent; /* don't need to check for root node here because root->parent is the sentinel nil node, and root->parent->left points back to root */ if (upper == upparent->left) { upparent->left = lower; } else { assert (upper == upparent->right); upparent->right = lower; } lower->left = upper; upper->parent = lower;}/* * This operation is the ``mirror'' image of rotate_left. It is * the same procedure, but with left and right interchanged. */static void rotate_right(dnode_t *upper){ dnode_t *lower, *lowright, *upparent; lower = upper->left; upper->left = lowright = lower->right; lowright->parent = upper; lower->parent = upparent = upper->parent; if (upper == upparent->right) { upparent->right = lower; } else { assert (upper == upparent->left); upparent->left = lower; } lower->right = upper; upper->parent = lower;}/* * Do a postorder traversal of the tree rooted at the specified * node and free everything under it. Used by dict_free(). */static void free_nodes(dict_t *dict, dnode_t *node, dnode_t *nil){ if (node == nil) return; free_nodes(dict, node->left, nil); free_nodes(dict, node->right, nil); dict->freenode(node, dict->context);}/* * This procedure performs a verification that the given subtree is a binary * search tree. It performs an inorder traversal of the tree using the * dict_next() successor function, verifying that the key of each node is * strictly lower than that of its successor, if duplicates are not allowed, * or lower or equal if duplicates are allowed. This function is used for * debugging purposes. */static int verify_bintree(dict_t *dict){ dnode_t *first, *next; first = dict_first(dict); if (dict->dupes) { while (first && (next = dict_next(dict, first))) { if (dict->compare(first->key, next->key) > 0) return 0; first = next; } } else { while (first && (next = dict_next(dict, first))) { if (dict->compare(first->key, next->key) >= 0) return 0; first = next; } } return 1;}/* * This function recursively verifies that the given binary subtree satisfies * three of the red black properties. It checks that every red node has only * black children. It makes sure that each node is either red or black. And it * checks that every path has the same count of black nodes from root to leaf. * It returns the blackheight of the given subtree; this allows blackheights to * be computed recursively and compared for left and right siblings for * mismatches. It does not check for every nil node being black, because there * is only one sentinel nil node. The return value of this function is the * black height of the subtree rooted at the node ``root'', or zero if the * subtree is not red-black. */static unsigned int verify_redblack(dnode_t *nil, dnode_t *root){ unsigned height_left, height_right; if (root != nil) { height_left = verify_redblack(nil, root->left); height_right = verify_redblack(nil, root->right); if (height_left == 0 || height_right == 0) return 0; if (height_left != height_right) return 0; if (root->color == dnode_red) { if (root->left->color != dnode_black) return 0; if (root->right->color != dnode_black) return 0; return height_left; } if (root->color != dnode_black) return 0; return height_left + 1; } return 1;}/* * Compute the actual count of nodes by traversing the tree and * return it. This could be compared against the stored count to * detect a mismatch. */static dictcount_t verify_node_count(dnode_t *nil, dnode_t *root){ if (root == nil) return 0; else return 1 + verify_node_count(nil, root->left) + verify_node_count(nil, root->right);}/* * Verify that the tree contains the given node. This is done by * traversing all of the nodes and comparing their pointers to the * given pointer. Returns 1 if the node is found, otherwise * returns zero. It is intended for debugging purposes. */static int verify_dict_has_node(dnode_t *nil, dnode_t *root, dnode_t *node){ if (root != nil) { return root == node || verify_dict_has_node(nil, root->left, node) || verify_dict_has_node(nil, root->right, node); } return 0;}/* * Dynamically allocate and initialize a dictionary object. */dict_t *dict_create(dictcount_t maxcount, dict_comp_t comp){ dict_t *new = malloc(sizeof *new); if (new) { new->compare = comp; new->allocnode = dnode_alloc; new->freenode = dnode_free; new->context = NULL; new->nodecount = 0; new->maxcount = maxcount; new->nilnode.left = &new->nilnode; new->nilnode.right = &new->nilnode; new->nilnode.parent = &new->nilnode; new->nilnode.color = dnode_black; new->dupes = 0; } return new;}/* * Select a different set of node allocator routines. */void dict_set_allocator(dict_t *dict, dnode_alloc_t al, dnode_free_t fr, void *context){ assert (dict_count(dict) == 0); assert ((al == NULL && fr == NULL) || (al != NULL && fr != NULL)); dict->allocnode = al ? al : dnode_alloc; dict->freenode = fr ? fr : dnode_free; dict->context = context;}/* * Free a dynamically allocated dictionary object. Removing the nodes * from the tree before deleting it is required. */void dict_destroy(dict_t *dict){ assert (dict_isempty(dict)); free(dict);}/* * Free all the nodes in the dictionary by using the dictionary's * installed free routine. The dictionary is emptied. */void dict_free_nodes(dict_t *dict){ dnode_t *nil = dict_nil(dict), *root = dict_root(dict); free_nodes(dict, root, nil); dict->nodecount = 0; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode;}/* * Obsolescent function, equivalent to dict_free_nodes */void dict_free(dict_t *dict){#ifdef KAZLIB_OBSOLESCENT_DEBUG assert ("call to obsolescent function dict_free()" && 0);#endif dict_free_nodes(dict);}/* * Initialize a user-supplied dictionary object. */dict_t *dict_init(dict_t *dict, dictcount_t maxcount, dict_comp_t comp){ dict->compare = comp; dict->allocnode = dnode_alloc; dict->freenode = dnode_free; dict->context = NULL; dict->nodecount = 0; dict->maxcount = maxcount; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; dict->nilnode.color = dnode_black; dict->dupes = 0; return dict;}/* * Initialize a dictionary in the likeness of another dictionary */void dict_init_like(dict_t *dict, const dict_t *template){ dict->compare = template->compare; dict->allocnode = template->allocnode; dict->freenode = template->freenode; dict->context = template->context; dict->nodecount = 0; dict->maxcount = template->maxcount; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; dict->nilnode.color = dnode_black; dict->dupes = template->dupes; assert (dict_similar(dict, template));}/* * Remove all nodes from the dictionary (without freeing them in any way). */static void dict_clear(dict_t *dict){ dict->nodecount = 0; dict->nilnode.left = &dict->nilnode; dict->nilnode.right = &dict->nilnode; dict->nilnode.parent = &dict->nilnode; assert (dict->nilnode.color == dnode_black);}/* * Verify the integrity of the dictionary structure. This is provided for * debugging purposes, and should be placed in assert statements. Just because * this function succeeds doesn't mean that the tree is not corrupt. Certain * corruptions in the tree may simply cause undefined behavior. */ int dict_verify(dict_t *dict){ dnode_t *nil = dict_nil(dict), *root = dict_root(dict); /* check that the sentinel node and root node are black */ if (root->color != dnode_black) return 0; if (nil->color != dnode_black) return 0; if (nil->right != nil) return 0; /* nil->left is the root node; check that its parent pointer is nil */ if (nil->left->parent != nil) return 0; /* perform a weak test that the tree is a binary search tree */ if (!verify_bintree(dict)) return 0; /* verify that the tree is a red-black tree */ if (!verify_redblack(nil, root)) return 0; if (verify_node_count(nil, root) != dict_count(dict)) return 0; return 1;}/* * Determine whether two dictionaries are similar: have the same comparison and * allocator functions, and same status as to whether duplicates are allowed. */int dict_similar(const dict_t *left, const dict_t *right){ if (left->compare != right->compare) return 0; if (left->allocnode != right->allocnode) return 0; if (left->freenode != right->freenode) return 0; if (left->context != right->context) return 0; if (left->dupes != right->dupes) return 0; return 1;}/* * Locate a node in the dictionary having the given key. * If the node is not found, a null a pointer is returned (rather than * a pointer that dictionary's nil sentinel node), otherwise a pointer to the * located node is returned. */dnode_t *dict_lookup(dict_t *dict, const void *key){ dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *saved; int result; /* simple binary search adapted for trees that contain duplicate keys */ while (root != nil) { result = dict->compare(key, root->key); if (result < 0) root = root->left; else if (result > 0) root = root->right; else { if (!dict->dupes) { /* no duplicates, return match */ return root; } else { /* could be dupes, find leftmost one */ do { saved = root; root = root->left; while (root != nil && dict->compare(key, root->key)) root = root->right; } while (root != nil); return saved; } } } return NULL;}/* * Look for the node corresponding to the lowest key that is equal to or * greater than the given key. If there is no such node, return null. */dnode_t *dict_lower_bound(dict_t *dict, const void *key){ dnode_t *root = dict_root(dict); dnode_t *nil = dict_nil(dict); dnode_t *tentative = 0; while (root != nil) { int result = dict->compare(key, root->key); if (result > 0) { root = root->right; } else if (result < 0) { tentative = root; root = root->left; } else { if (!dict->dupes) { return root; } else { tentative = root; root = root->left; } } } return tentative;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -