数据结构——树的遍历

本文介绍了树的三种遍历方法:中序遍历、先序遍历和后序遍历,并给出了它们在二叉查询树、表达式树中的应用。还提供了C语言和Java语言的实现示例,讨论了不同边界情况的时间复杂度,包括偏树和左右子树平衡的情况。
摘要由CSDN通过智能技术生成

原文地址:Tree Traversals

不像线性数据结构(数组,列表,队列,堆栈等),它们在逻辑上只有一种遍历方式,树可以有不同的遍历方式。下面就是遍历树的一般所用的方法:

![这里写图片描述](https://img-blog.csdn.net/20161126134534698) 深度优先遍历: 1)中序遍历(Left, Root, Right):4 2 5 1 3 2)先序遍历(Root, Left, Right):1 2 4 5 3 3)后序遍历(Left, Right, Root): 4 5 2 3 1 广度优先和层级遍历:1 2 3 4 5 **中序遍历:**
Algorithm Inorder(tree)
   1. Traverse the left subtree, i.e., call Inorder(left-subtree)
   2. Visit the root.
   3. Traverse the right subtree, i.e., call Inorder(right-subtree)

中序遍历应用:

在二叉查询树(BST)中,中序遍历是非递减遍历已知节点。可以通过中序遍历的一个变量得到BST的非递减顺序。

例如:上图的中序遍历是:4 2 5 1 3。

先序遍历:

Algorithm Preorder(tree)
   1. Visit the root.
   2. Traverse the left subtree, i.e., call Preorder(left-subtree)
   3. Traverse the right subtree, i.e., call Preorder(right-subtree) 

先序遍历的应用:

先序遍历可以用于建立一个树的拷贝。先序遍历也可以用于在表达式树中获取前缀表达式。请看http://en.wikipedia.org/wiki/Polish_notation就知道为啥前缀表达式是有用的了。

例如:上图的先序遍历是:1 2 4 5 3。

后续遍历:

Algorithm Postorder(tree)
   1. Traverse the left subtree, i.e., call Postorder(left-subtree)
   2. Traverse the right subtree, i.e., call Postorder(right-subtree)
   3. Visit the root.

后序遍历的应用:

后续遍历可以用于删除树。请看树的删除问题的详细描述。在一个表达式树中,后序遍历也可以用于获取后缀表达式。请看http://en.wikipedia.org/wiki/Reverse_Polish_notation,后缀表达式的用法。

例如:上图的后序遍历为:4 5 2 3 1。

C语言实现

// C program for different tree traversals
#include <stdio.h>
#include <stdlib.h>

/* A binary tree node has data, pointer to left child
   and a pointer to right child */
struct node
{
     int data;
     struct node* left;
     struct node* right;
};

/* Helper function that allocates a new node with the
   given data and NULL left and right pointers. */
struct node* newNode(int data)
{
     struct node* node = (struct node*)
                                  malloc(sizeof(struct node));
     node->data = data;
     node->left = NULL;
     node->right = NULL;

     return(node);
}

/* Given a binary tree, print its nodes according to the
  "bottom-up" postorder traversal. */
void printPostorder(struct node* node)
{
     if (node == NULL)
        return;

     // first recur on left subtree
     printPostorder(node->left);

     // then recur on right subtree
     printPostorder(node->right);

     // now deal with the node
     printf("%d ", node->data);
}

/* Given a binary tree, print its nodes in inorder*/
void printInorder(struct node* node)
{
     if (node == NULL)
          return;

     /* first recur on left child */
     printInorder(node->left);

     /* then print the data of node */
     printf("%d ", node->data);  

     /* now recur on right child */
     printInorder(node->right);
}

/* Given a binary tree, print its nodes in preorder*/
void printPreorder(struct node* node)
{
     if (node == NULL)
          return;

     /* first print data of node */
     printf("%d ", node->data);  

     /* then recur on left sutree */
     printPreorder(node->left);  

     /* now recur on right subtree */
     printPreorder(node->right);
}    

/* Driver program to test above functions*/
int main()
{
     struct node *root  = newNode(1);
     root->left             = newNode(2);
     root->right           = newNode(3);
     root->left->left     = newNode(4);
     root->left->right   = newNode(5); 

     printf("\nPreorder traversal of binary tree is \n");
     printPreorder(root);

     printf("\nInorder traversal of binary tree is \n");
     printInorder(root);  

     printf("\nPostorder traversal of binary tree is \n");
     printPostorder(root);

     getchar();
     return 0;
}

Java语言实现

// Java program for different tree traversals

/* Class containing left and right child of current
   node and key value*/
class Node
{
    int key;
    Node left, right;

    public Node(int item)
    {
        key = item;
        left = right = null;
    }
}

class BinaryTree
{
    // Root of Binary Tree
    Node root;

    BinaryTree()
    {
        root = null;
    }

    /* Given a binary tree, print its nodes according to the
      "bottom-up" postorder traversal. */
    void printPostorder(Node node)
    {
        if (node == null)
            return;

        // first recur on left subtree
        printPostorder(node.left);

        // then recur on right subtree
        printPostorder(node.right);

        // now deal with the node
        System.out.print(node.key + " ");
    }

    /* Given a binary tree, print its nodes in inorder*/
    void printInorder(Node node)
    {
        if (node == null)
            return;

        /* first recur on left child */
        printInorder(node.left);

        /* then print the data of node */
        System.out.print(node.key + " ");

        /* now recur on right child */
        printInorder(node.right);
    }

    /* Given a binary tree, print its nodes in preorder*/
    void printPreorder(Node node)
    {
        if (node == null)
            return;

        /* first print data of node */
        System.out.print(node.key + " ");

        /* then recur on left sutree */
        printPreorder(node.left);

        /* now recur on right subtree */
        printPreorder(node.right);
    }

    // Wrappers over above recursive functions
    void printPostorder()  {     printPostorder(root);  }
    void printInorder()    {     printInorder(root);   }
    void printPreorder()   {     printPreorder(root);  }

    // Driver method
    public static void main(String[] args)
    {
        BinaryTree tree = new BinaryTree();
        tree.root = new Node(1);
        tree.root.left = new Node(2);
        tree.root.right = new Node(3);
        tree.root.left.left = new Node(4);
        tree.root.left.right = new Node(5);

        System.out.println("Preorder traversal of binary tree is ");
        tree.printPreorder();

        System.out.println("\nInorder traversal of binary tree is ");
        tree.printInorder();

        System.out.println("\nPostorder traversal of binary tree is ");
        tree.printPostorder();
    }
}

输出:

Preorder traversal of binary tree is
1 2 4 5 3 
Inorder traversal of binary tree is
4 2 5 1 3 
Postorder traversal of binary tree is
4 5 2 3 1

另外一个例子:


这里写图片描述

图片地址:https://www.cs.swarthmore.edu/~newhall/unixhelp/Java_bst.pdf

时间复杂度:O(n)

我们看下不同的边界情况。

函数复杂度 T(n) ——对于遍历说涉及到的所有问题——可以定义为:

T(n) = T(k) + T(n – k – 1) + c

这里根一边的节点的数目,n-k-1是另一边的节点数。

我们分析一下边界条件:

情况1:偏树(Skewed tree)(其中的一个子树为空,而另一边的子树不为空)。

这种情况下k等于0。

T(n)=T(0)+T(n1)+c
T(n)=2T(0)+T(n2)+2c
T(n)=3T(0)+T(n3)+3c
T(n)=4T(0)+T(n4)+4c

…………………………………………
………………………………………….
T(n)=(n1)T(0)+T(1)+(n1)c
T(n)=nT(0)+(n)c

T(0)的值将是一个常数,比如d。(遍历一个空树的时间就是花费常量的时间)

T(n)=n(c+d)
T(n)=Θ(n)(Thetaofn)

情况2:左右子树的节点数目是相同的

T(n)=2T(n/2)+c

这个递归函数对于大师级的方法( http://en.wikipedia.org/wiki/Master_theorem)是标准形式( T(n)=aT(n/b)+()(n) )。如果我们用大师方法解决,那么会得到(-)(n)。

空间复杂度:如果我们不考虑栈的大小,那么函数调用是O(1) 否则是O(n)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值