二叉树结构
struct bst_node {
struct bst_node *parent;
struct bst_node *left;
struct bst_node *right;
int key;
};
1. 递归
这个代码最直接,不需要父结点,不多解释
void bst_inorder(struct bst_node *node)
{
if (node) {
if (node->left)
bst_inorder(node->left);
printf("%d ", node->key);
if (node->right)
bst_inorder(node->right);
}
}
void bst_postorder(struct bst_node *node)
{
if (node) {
if (node->left)
bst_postorder(node->left);
if (node->right)
bst_postorder(node->right);
printf("%d ", node->key);
}
}
void bst_preorder(struct bst_node *node)
{
if (node) {
printf("%d ", node->key);
if (node->left)
bst_preorder(node->left);
if (node->right)
bst_preorder(node->right);
}
}
2. 父结点
使用父结点回溯
2.1中序
void bst_nonrecur_inorder(struct bst_node *root)
{
struct bst_node *p = root, *parent;
while (p && p->left)
p = p->left;
for (;p;) {
printf("%d ", p->key);
if (p->right) {
p = p->right;
while (p && p->left)
p = p->left;
}
else {
parent = p->parent;
while (parent) {
if (parent->left == p) {
break;
}
p = parent;
parent = parent->parent;
}
p = parent;
}
}
}
遍历过程很简单,首先找到最左结点,即找根结点树的最小结点。
访问当前结点,如果当前结点有右子结点,则访问其右子结点的最左结点;否则回溯,直到某个祖先结点为其父结点的左子结点,此为后继结点;然后继续循环。
这个遍历过程就是算法导论中的两个过程,一个是取最小值,另一个是找后继结点
2.2 前序
void bst_nonrecur_preorder(struct bst_node *root)
{
struct bst_node *p = root, *parent;
while(p) {
printf("%d ", p->key);
if (p->left)
p = p->left;
else if (p->right)
p = p->right;
else {
for (;;) {
if (!(parent = p->parent)) {
p = NULL;
break;
}
if (parent->left == p && parent->right) {
p = parent->right;
break;
}
p = parent;
}
}
}
}
前序遍历最简单,首先访问当前结点,然后优先访问左子树,然后右子树,如果遇到叶子结点,则回溯,直到其祖先结点为其父结点的左子结点而且该祖先结点有右子树
2.3 后序
void bst_nonrecur_postorder(struct bst_node *root)
{
struct bst_node *p = root, *parent;
while(p) {
if (p->left)
p = p->left;
else if (p->right)
p = p->right;
else {
printf("%d ", p->key);
for (;;) {
if (!(parent = p->parent)) {
p = NULL;
break;
}
if (parent->left == p) {
if (parent->right) {
p = parent->right;
break;
}
printf("%d ", parent->key);
}
else if (parent->right == p) {
printf("%d ", parent->key);
}
p = parent;
}
}
}
}
后序遍历类似前序,只不过优先访问左子树,其次右子树,最后才是根结点。上述代码先访问叶子结点,在回溯的过程中,分两种情况讨论:
1. 该叶子结点在其某祖先结点的左子树,如果该祖先结点没有右子树,则直接访问该祖先结点,否则转向其右子树
2. 该叶子结点在其某祖先结点的右子树,由于从右子树回溯,表示左右子树都访问过了,直接访问该祖先结点
3. 栈
使用数组实现栈结构
3.1 中序
void bst_stack_inorder(struct bst_node *root)
{
int i = 0;
struct bst_node *s[1024] = {0, };
struct bst_node *p = root;
/* use s[0] as NULL pointer */
#define PUSH_S(n, i) s[++(i)]=(n)
#define POP_S(i) s[(i)--]
PUSH_S(p, i);
while (p->left) {
PUSH_S(p->left, i);
p = p->left;
}
while (i > 0) {
/* pop top && visit top var */
p = POP_S(i);
printf("%d ", p->key);
/* push right */
if (p->right) {
PUSH_S(p->right, i);
p = p->right;
/* push all left */
while (p->left) {
PUSH_S(p->left, i);
p = p->left;
}
}
}
}
遍历过程和使用父结点类似,只不过将回溯过程使用栈来处理
3.2 前序
void bst_stack_preorder(struct bst_node *root)
{
int i = 0;
struct bst_node *s[1024] = {0, };
struct bst_node *p = root;
/* use s[0] as NULL pointer */
#define PUSH_S(n, i) s[++(i)]=(n)
#define POP_S(i) s[(i)--]
PUSH_S(p, i);
while (i > 0) {
/* pop top && visit top var */
p = POP_S(i);
printf("%d ", p->key);
/* push right */
if (p->right) {
PUSH_S(p->right, i);
}
/* push left */
if (p->left)
PUSH_S(p->left, i);
}
}
此遍历过程最简单,首先访问当前结点,再将右子结点压栈,最后再压入左子结点,因为栈的后进先出原则保证优先访问左子结点
3.3 后序
static inline void bst_stack_post_push(struct bst_node *p, struct bst_node **s, int *i)
{
int index = *i;
s[++index] = p;
while (p->left || p->right) {
if (p->left)
p = p->left;
else
p = p->right;
s[++index] = p;
}
*i = index;
}
void bst_stack_postorder(struct bst_node *root)
{
int i = 0;
struct bst_node *s[1024] = {0, };
struct bst_node *p = root, *last;;
/* use s[0] as NULL pointer */
#define PUSH_S(n, i) s[++(i)]=(n)
#define POP_S(i) s[(i)--]
#define TOP_S(i) s[(i)]
bst_stack_post_push(p, s, &i);
while (i > 0) {
/* get top */
p = TOP_S(i);
if (p->left == last && p->right) {
bst_stack_post_push(p->right, s, &i);
}
else {
p = POP_S(i);
printf("%d ", p->key);
last = p;
}
}
}
此遍历过程最复杂,优先访问左子结点,其次右子结点,一直到叶子结点,将其所有祖先结点入栈,这个过程由函数bst_stack_post_push完成
使用last 来保存上一个遍历的结点
遍历时,先查看栈顶元素,分两种情况讨论:
1. 如果遍历路径是从左子树到根,则查看根结点是否有右子树,如果有右子树,则需要先将右子树使用bst_stack_post_push处理,否则直接访问