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

什么是二叉搜索树

一棵二叉搜索树是以一棵二叉树来组织的,如下图:
这里写图片描述
这样的一棵树可以使用一个链表数据结构来表示,其中每个结点就是一个对象。除了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 =
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值