二叉搜索树(遍历不递归,不用栈)

来源:互联网 发布:互联网金融发展 知乎 编辑:程序博客网 时间:2024/05/16 23:46

什么是二叉搜索树

一棵二叉搜索树是以一棵二叉树来组织的,如下图:
这里写图片描述
这样的一棵树可以使用一个链表数据结构来表示,其中每个结点就是一个对象。除了key和卫星数据之外,每个结点还包含属性left, right 和 parent, 它们分别指向左孩子,右孩子和双亲。比如:

typeof struct node{    int key;    // other data... 即卫星数据    struct node *lchild; //左孩子    struct node *rchild; //右孩子    struct node *parent; //双亲};

二叉搜索树,对任何结点x,其左子树中的关键字最大不超过x.key, 其中右子树中的关键字最小不低于x.key。不同的二叉搜索树可以代表同一组值的集合。大部分搜索树操作的最坏运行时间与树的高度成正比。如上图中(a)一棵包含6个结点高度为2的二叉搜索树。(b)一棵包含相同关键字、高度为4的低效二叉搜索树.
二叉搜索树中的关键字总是以满足二叉搜索树性质的方式来存储:
这是什么意思呢,设x为二叉搜索树中一结点,若y是x左子树中一结点,则y.keyx.key; 若y是x右子树中一结点,则y.keyx.key

二叉搜索树遍历

那么根据上面性质,我们可知,要想按序输出二叉搜索树中的所有关键字,需用中序遍历(inorder tree walk)算法.
inorder tree walk 伪代码如下:
INORDER-TREE-WALK(x) // x为根结点指针

if x != NIL    INORDER-TREE-WALK(x.left)    printf(x.key)    INORDER-TREE-WALK(x.right)

C/C++语言代码实现:
(1)非递归,用栈来实现,在我写的二叉树遍历里面有地址:
http://blog.csdn.net/jun2016425/article/details/72783252
(2)非递归,不用栈
一、中序遍历

步骤:

  1. 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。

  2. 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。

    a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。

    b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。

  3. 重复以上1、2直到当前节点为空。

图示:

下图为每一步迭代的结果(从左至右,从上到下),cur代表当前节点,深色节点表示该节点已输出。
这里写图片描述
注意:本来想用下面这个(1)结点结构的,但是写到请求前驱后继的时候有点难写,就用了有双亲的(2)结点结构。正如UNIX编程艺术里面说的:数据结构才是编程的核心,宁愿让数据结构复杂一点,也不愿省的一点两点的数据结构空间,而增大算法的复杂度,因为相比于算法逻辑的复杂,数据结构的复杂更容易让人看懂. so KISS(Keep It Simple, Stupid)
(1)

typedef struct _binary_search_tree{    int key;    struct _binary_search_tree *left;    struct _binary_search_tree *right;}binary_search_tree;

(2)

typedef struct _binary_search_tree{    int key;    struct _binary_search_tree *left;    struct _binary_search_tree *right;    struct _binary_search_tree *parent;}binary_search_tree;
void inorderMorrisTraversal(binary_search_tree *root){    binary_search_tree *cur = root, *prev = NULL;    while (cur != NULL){        if ( cur->left == NULL ){// 1.            printf("%d ", cur->key);            cur = cur->right;        }else{            // 查找当前结点的前驱,中序遍历中当前结点的前一个结点            prev = cur->left;            /* 查找当前结点的前驱 prev->right != NULL.             * 当前结点的左子树已经被遍历之后, prev->right != cur             * */            while ( prev->right != NULL && prev->right != cur )                prev = prev->right;            if (prev->right == NULL){ // 2.a 查找当前结点的前驱.                prev->right = cur;                cur = cur->left;            }else{ // 2.b 当前结点的左子树已经被遍历之后.                prev->right = NULL;                printf("%d ", cur->key);                cur = cur->right;            }        }    }}

二叉搜索树插入

将一个新值v插入到一棵二叉搜索树树T中,需要调用TREE-INSERT。该过程以结点z作为输入,其中z.key=v, z.left=NIL, z.right=NIL。
二叉搜索树插入伪代码:
TREE-INSERT(T,z)

y = NILx = T.rootwhile x != NIL    y = x    if z.key < x.key        x = x.left    else x = x.rightif y == NIL    T.root = z // tree was empty.else if z.key < y.key    y.left = zelse y.right = z

二叉搜索树插入C语言代码:

void TREE_INSERT(binary_search_tree *root, binary_search_tree *node){    binary_search_tree *prev = NULL, *cur = NULL;    prev = root;    while ( prev != NULL ){        cur = prev;        if ( node->key < prev->key )            prev = prev->left;        else            prev = prev->right;    }    if ( cur == NULL )        root = node; // tree was empty.    else if ( node->key < cur->key )        cur->left = node;    else        cur->right = node;}

二叉搜索树搜索

输入一个指向树根的指针和一个关键字k,如果这个结点存在,
TREE-SEARCH返回一个指向关键字为k的结点的指针,否则返回NIL
TREE-SEACH(x, k)

if x == NIL or k == x.key    return xif k < x.key    return TREE-SEACH(x.left, k)else return TREE-SEACH(x.right, k)

迭代方式:
这里可以采用while循环来展开递归,用一种迭代方式重写这个过程,
对于大多数计算机,迭代版本的效率要高得多。
TREE-SEACH(x, k)

while x != NIL and k != x.key    if k < x.key        x = x.left    else x = x.rightreturn x

二叉搜索树后继

binary_search_tree *TREE_SUCCESSOR(binary_search_tree *node){    binary_search_tree *prev = NULL;    binary_search_tree *cur = node;    /* 如果有右子树,则next为右子树的最左节点 */    if ( cur->right )        return TREE_MINIMUM( cur->right );    /* 无右子树的结点,只需简单的沿数而上直到遇到一个其双亲结点有左孩子的结点,    * 那么该双亲结点就是node的后继     */    prev = cur->parent;    while ( prev && cur == prev->right ){        cur = prev;        prev = prev->parent;    }    return prev;}

二叉搜索树前驱

binary_search_tree *TREE_PREDECESSOR(binary_search_tree *node){    binary_search_tree *prev = NULL;    binary_search_tree *cur = node;    /* 如果有左子树,则next为左子树的最右节点 */    if ( cur->left )        return TREE_MAXIMUM( cur->left );    /* 如果无左子树,则next在祖宗节点上,本节点为祖宗节点的右支 */    prev = cur->parent;    while ( prev && cur == prev->left ){        cur = prev;        prev = prev->parent;    }    return prev;}

C语言代码:

binary_search_tree *TREE_SEACH(binary_search_tree *root, int k){    binary_search_tree *tmp = root;    while ( tmp != NULL && k != tmp->key ){        printf("[%s][%d] tmp->key = %d\n",__FUNCTION__, __LINE__, tmp->key);        if ( k < tmp->key )            tmp = tmp->left;        else            tmp = tmp->right;    }    return tmp;}

二叉搜索树删除

替换

/* 用new_node 替换 old_node */void TRANSPLANT(binary_search_tree *root, binary_search_tree *old_node, binary_search_tree *new_node){    if ( NULL == old_node->parent ) // old_node 为树根        root = new_node;    else if ( old_node == old_node->parent->left ) // old_node 是双亲的左孩子        old_node->parent->left = new_node;    else        old_node->parent->right = new_node;// old_node 是双亲的右孩子    if ( new_node != NULL )        new_node->parent = old_node->parent;}

删除

/* 删除树中某个结点,且释放该结点资源,如果该结点存在 */void TREE_DELETE(binary_search_tree *root, binary_search_tree *node){    binary_search_tree *tmp = NULL;    /* node (即z) 没有左孩子        q                 q        |                 |        z     ----->      r        /\                /\    NULL  r          /\    */    if ( NULL == node->left )         TRANSPLANT(root, node, node->right);    /* node(即z) 没有右孩子        q                 q        |                 |        z     ----->      l        /\                /\       l  NULL       /\    */    else if ( NULL == node->right )        TRANSPLANT(root, node, node->left);    else{        tmp = TREE_MINIMUM(node->right); // 获取node右子树最小的结点tmp        /*         如果node(即z)的右子树的最小结点tmp(即y)不是node(z)的右孩子.        即z的后继是y,而且y还是不是z的右孩子,但是y是z右子树里面最小的,        所以y是没有左子树的.        则在以node->right(即r)为根的小子树(substree root)里面,        让(tmp)y被x(y的右孩子)取代,让tmp(y)成为node(即z)的右孩子(这里是r)的双亲结点,        让tmp(y)成为node->parent(q)的孩子,还有成为node->left(即l)的双亲.          q                 q                              q          |                 |                              |          z     ----->      z                 ------>      y             /\                /\                             /\       l     r             l            y                 l  r       /\    /\                         /\                   /\            y                       NULL  r                 x            /\                            /\                /\         NULL x                          x                                        /\        */        if (tmp->parent != node){             TRANSPLANT(root, tmp, tmp->right);            tmp->right = node->right;  // meaning y->right = r            tmp->right->parent = tmp;  // meaning r->parent = y        }        /*         如果node(即z)的右子树的最小结点tmp(即y)是node(z)的右孩子.          q                            q          |                            |          z               ------>      y             /\                           /\        l     y                       l  x       /\    / \                         /\           NULL x                                                     /\                                                   */        TRANSPLANT(root, node, tmp);        tmp->left = node->left;        tmp->left->parent = tmp;    }    if (node){        free(node);        node = NULL;    }}

完整的测试例子(C语言)

#include <stdio.h>#include <stdlib.h>typedef struct _binary_search_tree{    int key;    struct _binary_search_tree *left;    struct _binary_search_tree *right;    struct _binary_search_tree *parent;}binary_search_tree;static inline void INIT_NODE(binary_search_tree *node, int key){    node->key = key;    node->left = NULL;    node->right = NULL;    node->parent = NULL;}void inorderMorrisTraversal(binary_search_tree *root);void TREE_INSERT(binary_search_tree *root, binary_search_tree *node);binary_search_tree *CREATE_NODE(int key);void REMOVE_TREE(binary_search_tree *root);binary_search_tree *TREE_SEACH(binary_search_tree *root, int k);binary_search_tree *TREE_MINIMUM(binary_search_tree *root){    binary_search_tree *node = root;    while ( node->left )        node = node->left;    return node;}binary_search_tree *TREE_MAXIMUM(binary_search_tree *root){    binary_search_tree *node = root;    while ( node->right )        node = node->right;    return node;}binary_search_tree *TREE_SUCCESSOR(binary_search_tree *node);binary_search_tree *TREE_PREDECESSOR(binary_search_tree *node);/* 用new_node 替换 old_node */void TRANSPLANT(binary_search_tree *root, binary_search_tree *old_node, binary_search_tree *new_node);/* 删除树中某个结点 */void TREE_DELETE(binary_search_tree *root, binary_search_tree *node);int main(void){    int i = 0;    binary_search_tree *root = NULL;    binary_search_tree *node = NULL;    binary_search_tree *position = NULL;    root = CREATE_NODE(6); // create root node    node = CREATE_NODE(9);    TREE_INSERT(root, node);    node = CREATE_NODE(8);    TREE_INSERT(root, node);        node = CREATE_NODE(1);    TREE_INSERT(root, node);    node = CREATE_NODE(2);    TREE_INSERT(root, node);    node = CREATE_NODE(5);    TREE_INSERT(root, node);    node = CREATE_NODE(4);    TREE_INSERT(root, node);    node = CREATE_NODE(3);    TREE_INSERT(root, node);    node = CREATE_NODE(7);    TREE_INSERT(root, node);    /* 查找特定结点 */    TREE_SEACH(root, 8);    /* 查找最大,最小结点 */    node = TREE_MAXIMUM(root);    printf("maximun number = %d\n", node->key);    node = TREE_MINIMUM(root);    printf("minimum number = %d\n", node->key);    /* 查找后继结点 */    position = TREE_SEACH(root, 9);    node = TREE_SUCCESSOR(position);    if (node == NULL)        printf("[%s][%d] %d is the last node no successor!!!\n", __FUNCTION__, __LINE__, position->key);    else         printf("[%s][%d] %d successor is %d\n", __FUNCTION__, __LINE__, position->key, node->key);    for (i=1; i<=9; ++i){    position = TREE_SEACH(root, i);    node = TREE_SUCCESSOR(position);    if (node == NULL)        printf("[%s][%d] %d is the last node no successor!!!\n", __FUNCTION__, __LINE__, position->key);    else         printf("[%s][%d] %d successor is %d\n", __FUNCTION__, __LINE__, position->key, node->key);    }    for (i=1; i<=9; ++i){    position = TREE_SEACH(root, i);    node = TREE_PREDECESSOR(position);    if (node == NULL)        printf("[%s][%d] %d is the last node no predecessor!!!\n", __FUNCTION__, __LINE__, position->key);    else         printf("[%s][%d] %d predecessor is %d\n", __FUNCTION__, __LINE__, position->key, node->key);    }    /* 中序遍历 */    inorderMorrisTraversal(root);    printf("\n");    /* 删除结点 */    node = TREE_SEACH(root, 3);    printf("after delete node:%d\n", node->key);    TREE_DELETE(root, node);    inorderMorrisTraversal(root);    printf("\n");    REMOVE_TREE(root);    return 0;}void inorderMorrisTraversal(binary_search_tree *root){    if (NULL == root)        printf("[%s][%d] root is null!!!\n", __FUNCTION__, __LINE__);    binary_search_tree *cur = root, *prev = NULL;    while (cur != NULL){        if ( cur->left == NULL ){            printf("%d ", cur->key);            cur = cur->right;        }else{            // find predecessor            prev = cur->left;            /* when find predecessor prev->right != NULL.             * when cur->left tree was walked.              * */            while ( prev->right != NULL && prev->right != cur )                prev = prev->right;            if (prev->right == NULL){ // find predecessor.                prev->right = cur;                cur = cur->left;            }else{ // cur->left tree was walked.                prev->right = NULL;                printf("%d ", cur->key);                cur = cur->right;            }        }    }}void TREE_INSERT(binary_search_tree *root, binary_search_tree *node){    binary_search_tree *prev = NULL, *cur = NULL;    prev = root;    while ( prev != NULL ){        cur = prev;        if ( node->key < prev->key )            prev = prev->left;        else            prev = prev->right;    }    node->parent = cur;    if ( cur == NULL )        root = node; // tree was empty.    else if ( node->key < cur->key )        cur->left = node;    else        cur->right = node;}binary_search_tree *CREATE_NODE(int key){    binary_search_tree *node = malloc(sizeof(binary_search_tree));    if ( node == NULL ){//malloc failed.        printf("malloce memory failed!!!\n");        return node;    }    INIT_NODE(node, key);    return node;}void REMOVE_TREE(binary_search_tree *root){    if (root == NULL)        return;    REMOVE_TREE(root->left);    REMOVE_TREE(root->right);    free(root);    root = NULL;    return;}binary_search_tree *TREE_SEACH(binary_search_tree *root, int k){    binary_search_tree *tmp = root;    while ( tmp != NULL && k != tmp->key ){        if ( k < tmp->key )            tmp = tmp->left;        else            tmp = tmp->right;    }    return tmp;}binary_search_tree *TREE_SUCCESSOR(binary_search_tree *node){    binary_search_tree *prev = NULL;    binary_search_tree *cur = node;    /* 如果有右子树,则next为右子树的最左节点 */    if ( cur->right )        return TREE_MINIMUM( cur->right );    /* 无右子树的结点,只需简单的沿数而上直到遇到一个其双亲结点有左孩子的结点,    * 那么该双亲结点就是node的后继     */    prev = cur->parent;    while ( prev && cur == prev->right ){        cur = prev;        prev = prev->parent;    }    return prev;}binary_search_tree *TREE_PREDECESSOR(binary_search_tree *node){    binary_search_tree *prev = NULL;    binary_search_tree *cur = node;    /* 如果有左子树,则next为左子树的最右节点 */    if ( cur->left )        return TREE_MAXIMUM( cur->left );    /* 如果无左子树,则next在祖宗节点上,本节点为祖宗节点的右支 */    prev = cur->parent;    while ( prev && cur == prev->left ){        cur = prev;        prev = prev->parent;    }    return prev;}/* 用new_node 替换 old_node */void TRANSPLANT(binary_search_tree *root, binary_search_tree *old_node, binary_search_tree *new_node){    if ( NULL == old_node->parent ) // old_node 为树根        root = new_node;    else if ( old_node == old_node->parent->left ) // old_node 是双亲的左孩子        old_node->parent->left = new_node;    else        old_node->parent->right = new_node;// old_node 是双亲的右孩子    if ( new_node != NULL )        new_node->parent = old_node->parent;}/* 删除树中某个结点,且释放该结点资源,如果该结点存在 */void TREE_DELETE(binary_search_tree *root, binary_search_tree *node){    binary_search_tree *tmp = NULL;    /* node (即z) 没有左孩子        q                 q        |                 |        z     ----->      r        /\                /\    NULL  r          /\    */    if ( NULL == node->left )         TRANSPLANT(root, node, node->right);    /* node(即z) 没有右孩子        q                 q        |                 |        z     ----->      l        /\                /\       l  NULL       /\    */    else if ( NULL == node->right )        TRANSPLANT(root, node, node->left);    else{        tmp = TREE_MINIMUM(node->right); // 获取node右子树最小的结点tmp        /*         如果node(即z)的右子树的最小结点tmp(即y)不是node(z)的右孩子.        即z的后继是y,而且y还是不是z的右孩子,但是y是z右子树里面最小的,        所以y是没有左子树的.        则在以node->right(即r)为根的小子树(substree root)里面,        让(tmp)y被x(y的右孩子)取代,让tmp(y)成为node(即z)的右孩子(这里是r)的双亲结点,        让tmp(y)成为node->parent(q)的孩子,还有成为node->left(即l)的双亲.          q                 q                              q          |                 |                              |          z     ----->      z                 ------>      y             /\                /\                             /\       l     r             l            y                 l  r       /\    /\                         /\                   /\            y                       NULL  r                 x            /\                            /\                /\         NULL x                          x                                        /\        */        if (tmp->parent != node){             TRANSPLANT(root, tmp, tmp->right);            tmp->right = node->right;  // meaning y->right = r            tmp->right->parent = tmp;  // meaning r->parent = y        }        /*         如果node(即z)的右子树的最小结点tmp(即y)是node(z)的右孩子.          q                            q          |                            |          z               ------>      y             /\                           /\        l     y                       l  x       /\    / \                         /\           NULL x                                                     /\                                                   */        TRANSPLANT(root, node, tmp);        tmp->left = node->left;        tmp->left->parent = tmp;    }    if (node){        free(node);        node = NULL;    }}

参考:
[1] http://www.cnblogs.com/AnnieKim/archive/2013/06/15/MorrisTraversal.html

阅读全文
0 0
原创粉丝点击