详细设计红黑树 - 4

242 阅读4分钟

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

最终设计

#ifndef UTIL_RBTREE_H
#define UTIL_RBTREE_H

typedef struct rbtree_node_t_ rbtree_node_t;
typedef struct rbtree_t_ rbtree_t;

struct rbtree_node_t_ {
	int key;
	unsigned char color;
	void *data;
	rbtree_node_t *left;
	rbtree_node_t *right;
	rbtree_node_t *parent;
};

struct rbtree_t_ {
	rbtree_node_t *root;        // the pointer of the root of the tree
	rbtree_node_t *sentinel;    // sentinel as black leaf node
	int (*compare)(void *, const void *);
	void (*destroy)(void *);
};

/* macros */
#define rbtree_set_compare_func(tree, func) (tree)->compare = func
#define rbtree_set_destroy_func(tree, func) (tree)->destroy = func

#define rbtree_red(node) ((node)->color = 1)
#define rbtree_black(node) ((node)->color = 0)
#define rbtree_is_red(node) ((node)->color)
#define rbtree_is_black(node) (!((node)->color))
#define rbtree_is_empty(tree) ((tree)->root == (tree)->sentinel)
// sentinel for black leaf node
#define rbtree_sentinel_init(node) rbtree_black(node)

// public methods
void rbtree_init(rbtree_t *tree, rbtree_node_t *sentinel);
void rbtree_insert_value(rbtree_t *tree, rbtree_node_t *node);
void rbtree_insert(rbtree_t *tree, rbtree_node_t *node);
void rbtree_delete(rbtree_t *tree, rbtree_node_t *node);
rbtree_node_t *rbtree_search_by_key(rbtree_t *tree, int key);
rbtree_node_t *rbtree_next(rbtree_t *tree, rbtree_node_t *node);
rbtree_node_t *rbtree_search_by_data(rbtree_t *tree, const void *keydata);
void *rbtree_get_data_by_data(rbtree_t *tree, const void *keydata);
void *rbtree_get_data_by_key(rbtree_t *tree, int key);
void rbtree_destory(rbtree_t *tree);

/* get the minimum key of node in a subtree of the rbtree */
rbtree_node_t *rbtree_get_min(rbtree_t *tree, rbtree_node_t *node);

#endif

···


## 实现文件
```c
#include <stdio.h>
#include "../include/yutil/rbtree.h"

static inline void rbtree_clear(rbtree_node_t *node)
{
	node->left = NULL;
	node->right = NULL;
	node->parent = NULL;
	node->data = NULL;
	node->key = 0;
}
/** 红黑树查找结点 */
static rbtree_node_t *rbtree_search_(rbtree_t *tree, int key,
				     const void *keydata)
{
	rbtree_node_t *node = tree->root;
	int ret;

	while (node != tree->sentinel) {
		if (tree->compare && keydata) {
			ret = tree->compare(node->data, keydata);
			if (ret == 0) {
				return node;
			}
			node = (ret < 0) ? node->right : node->left;
			continue;
		}
		if (key == node->key) {
			return node;
		}

		node = (key < node->key) ? node->left : node->right;
	}
	return NULL;
}

// repalce old_node in the tree by new_node
static inline void rbtree_replace_(rbtree_t *tree, rbtree_node_t *old_node,
				   rbtree_node_t *new_node)
{
	new_node->parent = old_node->parent;

	// repalce
	if (old_node == tree->root) {
		tree->root = new_node;
	} else if (old_node == old_node->parent->left) {
		old_node->parent->left = new_node;
	} else {
		old_node->parent->right = new_node;
	}

	old_node->parent = new_node;
}

/**
 * left rotate
 *
 *      node              temp
 *      / \       ==>     /  \
 *     a  temp         node  y
 *    / \              / \
 *   b   y            a   b
 */
static inline void rbtree_left_rotate(rbtree_t *tree, rbtree_node_t *node)
{
	rbtree_node_t *temp = node->right;

	// replace temp with left[temp]
	node->right = temp->left;
	// if left[temp] is not NIL, then it has a parent
	if (temp->left != tree->sentinel) {
		temp->left->parent = node;
	}

	// replace node with temp
	rbtree_replace_(tree, node, temp);
	temp->left = node;
}

/**
 * right rotate
 *
 *        node           temp
 *        /  \           /  \
 *      temp  y   ==>   a   node
 *      / \            / \
 *     a   b          b   y
 */
static inline void rbtree_right_rotate(rbtree_t *tree, rbtree_node_t *node)
{
	rbtree_node_t *temp = node->left;

	node->left = temp->right;
	if (temp->right != tree->sentinel) {
		temp->right->parent = node;
	}

	rbtree_replace_(tree, node, temp);
	temp->right = node;
}

static void rbtree_insert_rebalance(rbtree_t *tree, rbtree_node_t *node)
{
	rbtree_node_t *gparent;
	rbtree_node_t *uncle;
	while (node != tree->root && rbtree_is_red(node->parent)) {
		gparent = node->parent->parent;
		if (node->parent == gparent->left) {
			uncle = gparent->right;
			// case 1: uncle is red, then update node to gparent
			// case 2: uncle is balck or NIL
			if (rbtree_is_red(uncle)) {
				rbtree_black(node->parent);
				rbtree_black(uncle);
				rbtree_red(gparent);
				node = gparent;

			} else {
				// ensue node is the left kid, make gparent,
				// parent, node in one line
				if (node == node->parent->right) {
					node = node->parent;
					rbtree_left_rotate(tree, node);
					gparent = node->parent->parent;
				}
				// case 2 -> case 1
				rbtree_black(node->parent);
				rbtree_red(gparent);
				rbtree_right_rotate(tree, gparent);
			}
		} else {
			uncle = node->parent->parent->left;

			if (rbtree_is_red(uncle)) {
				rbtree_black(node->parent);
				rbtree_black(uncle);
				rbtree_red(gparent);
				node = gparent;
			} else {
				if (node == node->parent->left) {
					node = node->parent;
					rbtree_right_rotate(tree, node);
					gparent = node->parent->parent;
				}

				rbtree_black(node->parent);
				rbtree_red(gparent);
				rbtree_left_rotate(tree, gparent);
			}
		}
	}

	// ensure the root being black, useful when new node is root
	rbtree_black(tree->root);
}

static void rbtree_delete_rebalance(rbtree_t *tree, rbtree_node_t *node)
{
	rbtree_node_t *brother;
	while (node != tree->root && rbtree_is_black(node)) {
		if (node == node->parent->left) {
			brother = node->parent->right;

			if (rbtree_is_red(brother)) {
				rbtree_black(brother);
				rbtree_red(node->parent);
				rbtree_left_rotate(tree, node->parent);
				brother = node->parent->right;
			}

			if (rbtree_is_black(brother->left) &&
			    rbtree_is_black(brother->right)) {
				rbtree_red(brother);
				node = node->parent;

			} else {
				if (rbtree_is_black(brother->right)) {
					rbtree_black(brother->left);
					rbtree_red(brother);
					rbtree_right_rotate(tree, brother);
					brother = node->parent->right;
				}
				brother->color = node->parent->color;
				rbtree_black(node->parent);
				rbtree_black(brother->right);
				rbtree_left_rotate(tree, node->parent);
				node = tree->root;
			}

		} else {
			brother = node->parent->left;

			if (rbtree_is_red(brother)) {
				rbtree_black(brother);
				rbtree_red(node->parent);
				rbtree_right_rotate(tree, node->parent);
				brother = node->parent->left;
			}

			if (rbtree_is_black(brother->left) &&
			    rbtree_is_black(brother->right)) {
				rbtree_red(brother);
				node = node->parent;

			} else {
				if (rbtree_is_black(brother->left)) {
					rbtree_black(brother->right);
					rbtree_red(brother);
					rbtree_left_rotate(tree, brother);
					brother = node->parent->left;
				}

				brother->color = node->parent->color;
				rbtree_black(node->parent);
				rbtree_black(brother->left);
				rbtree_right_rotate(tree, node->parent);
				node = tree->root;
			}
		}
	}

	rbtree_black(node);
}

void rbtree_insert_value(rbtree_t *tree, rbtree_node_t *node)
{
	rbtree_node_t **temp = &tree->root;
	rbtree_node_t *parent = NULL;

	while (*temp != tree->sentinel) {
		parent = *temp;
		temp =
		    (node->key < parent->key) ? &parent->left : &parent->right;
	}

	*temp = node;
	node->parent = parent;
	node->left = tree->sentinel;
	node->right = tree->sentinel;
	rbtree_red(node);
}

void rbtree_init(rbtree_t *tree, rbtree_node_t *sentinel)
{
	rbtree_sentinel_init(sentinel);
	tree->root = sentinel;
	tree->sentinel = sentinel;
	tree->compare = NULL;
	tree->destroy = NULL;
}

void rbtree_insert(rbtree_t *tree, rbtree_node_t *node)
{
	rbtree_node_t *sentinel = tree->sentinel;
	rbtree_node_t *temp = rbtree_search_(tree, node->key, node->data);

	if (temp)
		return;

	// if tree is empty
	if (rbtree_is_empty(tree)) {
		node->parent = NULL;
		node->left = sentinel;
		node->right = sentinel;
		rbtree_black(node);
		tree->root = node;

		return;
	}

	rbtree_insert_value(tree, node);

	/* re-balance tree */
	rbtree_insert_rebalance(tree, node);
}

void rbtree_delete(rbtree_t *tree, rbtree_node_t *node)
{
	rbtree_node_t *subst;
	rbtree_node_t *temp;
	rbtree_node_t *sentinel = tree->sentinel;
	int red;
	/* a binary tree delete */
	if (rbtree_is_empty(tree) || node == sentinel) {
		return;
	}

	if (node->left == sentinel) {
		temp = node->right;
		subst = node;

	} else if (node->right == sentinel) {
		temp = node->left;
		subst = node;

	} else {
		subst = rbtree_get_min(tree, node->right);
		temp = subst->right;
	}

	if (subst == tree->root) {
		tree->root = temp;
		rbtree_black(temp);
		if (tree->destroy && node->data) {
			tree->destroy(node->data);
		}
		rbtree_clear(node);

		return;
	}

	red = rbtree_is_red(subst);

	if (subst == subst->parent->left) {
		subst->parent->left = temp;

	} else {
		subst->parent->right = temp;
	}

	// if subst is the node to delete
	if (subst == node) {
		temp->parent = subst->parent;

	} else {
		if (subst->parent == node) {
			temp->parent = subst;

		} else {
			temp->parent = subst->parent;
		}

		subst->left = node->left;
		subst->right = node->right;
		subst->parent = node->parent;
		subst->color = node->color;

		if (node == tree->root) {
			tree->root = subst;

		} else {
			if (node == node->parent->left) {
				node->parent->left = subst;
			} else {
				node->parent->right = subst;
			}
		}

		if (subst->left != sentinel) {
			subst->left->parent = subst;
		}

		if (subst->right != sentinel) {
			subst->right->parent = subst;
		}
	}

	if (tree->destroy && node->data) {
		tree->destroy(node->data);
	}
	rbtree_clear(node);

	if (!red) {
		rbtree_delete_rebalance(tree, temp);
	}

	return;
}

rbtree_node_t *rbtree_next(rbtree_t *tree, rbtree_node_t *node)
{
	rbtree_node_t *parent;
	if (node == tree->root) {
		return NULL;
	}
	if (node->right != tree->sentinel) {
		return rbtree_get_min(tree, node->right);
	}
	while ((parent = node->parent) && node == parent->right) {
		node = parent;
	}
	return parent;
}

static i = 0;
static void rbtree_node_destroy(rbtree_t *tree, rbtree_node_t *node)
{
	if (node == tree->sentinel) {
		return;
	}
	rbtree_node_destroy(tree, node->left);
	rbtree_node_destroy(tree, node->right);
	if (tree->destroy && node->data) {
		tree->destroy(node->data);
	}
	if (node->parent) {
		if (node->parent->left == node) {
			node->parent->left = tree->sentinel;
		} else {
			node->parent->right = tree->sentinel;
		}
	}
	rbtree_clear(node);
	// somthing wrong
}

void rbtree_destory(rbtree_t *tree)
{
	rbtree_node_destroy(tree, tree->root);
	tree->root = NULL;
}
rbtree_node_t *rbtree_get_min(rbtree_t *tree, rbtree_node_t *node)
{
	while (node->left != tree->sentinel) {
		node = node->left;
	}

	return node;
}

rbtree_node_t *rbtree_search_by_key(rbtree_t *tree, int key)
{
	return rbtree_search_(tree, key, NULL);
}
rbtree_node_t *rbtree_search_by_data(rbtree_t *tree, const void *keydata)
{
	return rbtree_search_(tree, 0, keydata);
}

void *rbtree_get_data_by_data(rbtree_t *tree, const void *keydata)
{
	rbtree_node_t *node;
	node = rbtree_search_by_data(tree, keydata, tree->compare);
	if (node) {
		return node->data;
	}
	return NULL;
}

void *rbtree_get_data_by_key(rbtree_t *tree, int key)
{
	rbtree_node_t *node;
	node = rbtree_search_by_key(tree, key);
	if (node) {
		return node->data;
	}
	return NULL;
}

实现完之后就会发现一个问题,虽然在实际的定义中,应该确保所有的子节点都是黑的,但在实际使用的时候,完全可以把哨兵结点换成NULL结点,这样既节省了空间,也在很多地方大大加快了判断速度等。 但在我实际操作的过程中,发现直接使用哨兵结点的红黑树不能直接将哨兵换成NULL结点,不然会出现删除中访问空节点的报错,留到之后解决吧。