Inorder Traversal a BST with Constant Extra Space

1. Modify the BST During the Traversal

出自:
http://stackoverflow.com/questions/5496464/write-a-non-recursive-traversal-of-a-binary-search-tree-using-constant-space-and

Use O(n) time and canstant space.

Idea:
Maintain two points of the nodes - parent and current. When processing, set parent.right to current.right, and set current.right to parent.

Java代码

void traverse (Node root) {
  traverse (root.left, root);
}

void traverse (Node current, Node parent) {
  while (current != null) {
    if (parent != null) {
      parent.left = current.right;
      current.right = parent;
    }

    if (current.left != null) {
      // go left until the left child is null
      parent = current;
      current = current.left;
    } else {
      // the nodes in the left subtree
      // are all visited, print and go right.
      print(current);
      current = current.right;
      parent = null;
    }
  }
}

示意图:
1.对左子树的处理
left-child
图中彩色虚线为更改之前的指针;
彩色实线为更改之后的指针。

2.对右子树的处理
right-child
执行过程中,节点left child的指针会依次指向其左子树中最右侧一列的每个节点。

Morris Traversal

参考:
http://comsci.liu.edu/~murali/algo/Morris.htm
这里有一个比较具体的解释:
http://stackoverflow.com/questions/5502916/explain-morris-inorder-tree-traversal-without-using-stacks-or-recursion

该算法需要O(n)时间和O(1)空间,且不会破坏BST的数据结构(在执行中会暂时改变个别节点的连接)。

主要思想是利用每个子树中最大(最右)的节点,该节点的right child一定为空,且在inorder traversal顺序中,该节点是该子树root节点的直接前驱。将该节点的right child暂时的设置为该子树的root节点,则在遍历过程中可以自然地从左子树过度到根节点,并递归地遍历右子树。
Reset the rightmost node in the current subtree(还原改变的节点): 只需在print某个节点时(第二次访问该节点),再次寻找该节点的直接前驱,并将其right child置为null。

//  Morris Traversal 

struct Node { 
    int data;
    struct Node *left, *right;
    int mark;
};

void MorrisTraversal(struct Node *root) {

    struct Node *current, *pre;

    if (root == NULL) 
        return;

    current = root;
    while (current != NULL) {
        if (current->left == NULL) { 
            printf(" %d ", current->data);
            current = current->right;
        } else {
            // find the predecessor of current 
            // (rightmost node in the subtree)
            pre = current->left;
            while (pre->right != NULL && pre->right != current)
                pre = pre->right;

            if(pre->right == NULL) {
                // firt visit the predecessor node
                pre->right = current;
                current = current->left;
            } else {
                // revisit, reset predecessor's right child
                pre->right = NULL;
                printf(" %d ", current->data);
                current = current->right;
            }


        }
    }
}

示意图
morris
第一次访问的标志:pre->right == NULL

第一次访问节点0时,添加4->0的虚线所表示的指针(节点4的right child);
第一次访问节点1时,添加3->1的虚线所表示的指针;
第一次访问节点2时,添加5->2的虚线所表示的指针。

第二次访问的标志:pre->right == current
第二次访问节点0时,删除指针4->0;
第二次访问节点0时,删除指针3->1;
第二次访问节点0时,删除指针5->2。

时间复杂度

Mirros Traversal 花费O(n)时间,其中最关键的步骤在于它需要(两次)查找一个节点的直接前驱,对应的代码为

while (pre->right != NULL && pre->right != current)
    pre = pre->right;

实际上,
查找每个节点的直接前驱所需遍历的边的数量 ~ 图中所有指向right child的边数 ~ N(# of nodes)
因此该步骤所需时间为O(n)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值