06 — Linux内核数据结构 — 红黑树 — 遍历(代码)

二叉树遍历中,以根节点的顺序为准,例如“根 —> 左 —> 右”就是前序遍历,“左 —> 右 —> 根”就是后序遍历,从左到右一层一层的遍历就是层序遍历。Linux红黑树主要提供中序和后序两种方法。

1. 中序遍历

左 —> 根 —> 右

1
2
3
4
struct rb_node *rb_first(const struct rb_root *);  //  中序遍历的最小节点,最左边
struct rb_node *rb_last(const struct rb_root *);   //  中序遍历的最大节点,最右边
struct rb_node *rb_next(const struct rb_node *);   //  中序遍历的后继节点
struct rb_node *rb_prev(const struct rb_node *);   //  中序遍历的前驱节点

找到最小节点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct rb_node *rb_first(const struct rb_root *root)
{
	struct rb_node	*n;

	n = root->rb_node;
	if (!n)
		return NULL;
	while (n->rb_left)  // 没有左孩子就对了
		n = n->rb_left;
	return n;
}

找到最大节点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
struct rb_node *rb_last(const struct rb_root *root)
{
	struct rb_node	*n;

	n = root->rb_node;
	if (!n)
		return NULL;
	while (n->rb_right)  // 没有右孩子就对了
		n = n->rb_right;
	return n;
}

node的后继节点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
struct rb_node *rb_next(const struct rb_node *node)
{
	struct rb_node *parent;

	if (RB_EMPTY_NODE(node))
		return NULL;

	if (node->rb_right) {
        // 1. 如果node有右子树,下一个节点就是右子树中最左节点。
		node = node->rb_right;   
		while (node->rb_left)
			node = node->rb_left;
		return (struct rb_node *)node;
	}

    // 2. 没有右子树,需要向上回溯,并更新当前节点以及其父节点,直到找到一个父节点,当前node节点是父节点的左孩子,这个父节点就是要找到后继节点,这是搜索二叉树的排序性质。  
	while ((parent = rb_parent(node)) && node == parent->rb_right)
		node = parent;

	return parent;
}

前驱节点:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
struct rb_node *rb_prev(const struct rb_node *node)
{
	struct rb_node *parent;

	if (RB_EMPTY_NODE(node))
		return NULL;

	if (node->rb_left) {
        // 1. 如果node有左子树,左子树中的最右节点就是后继节点
		node = node->rb_left;
		while (node->rb_right)
			node = node->rb_right;
		return (struct rb_node *)node;
	}

    // 2. node没有左子树,需要向上回溯,并更新当前节点以及其父节点,直到找到一个父节点,当前的node节点是父节点的右节点,这个父节点就是要找到后继节点。  
	while ((parent = rb_parent(node)) && node == parent->rb_left)
		node = parent;

	return parent;
}

2. 后序遍历

左 —> 右 —> 根

1
2
struct rb_node *rb_first_postorder(const struct rb_root *);  // 获取红黑树后序遍历的第一个节点,要符合“左 —> 右 —> 根”的规则
struct rb_node *rb_next_postorder(const struct rb_node *);   // 获取当前节点在后序遍历中的后继节点

获取红黑树后序遍历的第一个节点: 和中序遍历是一个节点。

1
2
3
4
5
6
7
struct rb_node *rb_first_postorder(const struct rb_root *root)
{
	if (!root->rb_node)
		return NULL;

	return rb_left_deepest_node(root->rb_node); // 找到树左侧的最深节点。
}

后序遍历中的后继节点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
struct rb_node *rb_next_postorder(const struct rb_node *node)
{
	const struct rb_node *parent;
	if (!node)
		return NULL;
	parent = rb_parent(node);

	if (parent && node == parent->rb_left && parent->rb_right) {
        // node是父节点的左孩子,由于 “左 —> 右 —> 根” 的要求,所以到父节点的右子树中,寻找左侧的最深节点
		return rb_left_deepest_node(parent->rb_right);
	} else {
        // 1. 父节点是NULL,说明node是根节点,此时返回NULL。  
        // 2. 父节点没有右子树,后继节点就是父节点。  
        // 3. node是父节点的右孩子,后继节点就是父节点。  
		return (struct rb_node *)parent;
    }
}

给定一个节点,找到该节点左侧的最深节点

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
static struct rb_node *rb_left_deepest_node(const struct rb_node *node)
{
    // 遍历中,如果节点有左子树,那就进入左子树,如果没有左子树,那就进入右子树,直到找到一个没有孩子的节点
	for (;;) {
		if (node->rb_left)
			node = node->rb_left;
		else if (node->rb_right)
			node = node->rb_right;
		else
			return (struct rb_node *)node;
	}
}

内核中没有用搜到后序遍历的使用,等以后需要的时候再去研究。

comments powered by Disqus