简体   繁体   English

如何从中序和前序遍历中生成二叉树

[英]how to make Binary Tree from inorder and preorder traversals

Here is the full question:这是完整的问题:

Write a function that gets two arrays of length n.编写一个 function 得到两个长度为 n 的 arrays。 The first array is the PreOrder some binary tree and the second array is the InOrder of the binary tree.第一个数组是 PreOrder 一些二叉树,第二个数组是二叉树的 InOrder。 The functions outputs the binary tree.函数输出二叉树。

// the function recovers the tree from its inorder and preorder
BTnode_t* reconstruct_tree( int * preorder, int * inorder, int n)

given struct and functions:给定结构和功能:

struct BTnode {
   int value;
   struct BTnode* left;
   struct BTnode* right;
   struct BTnode* parent;
};
typedef struct BTnode BTnode_t;

BTnode_t* create_node(int val) {
    BTnode_t* newNode = (BTnode_t*) malloc(sizeof(BTnode_t));
    newNode->value = val;
    newNode->left = NULL;
    newNode->right = NULL;
    newNode->parent = NULL;

    return newNode;
}

My implementation of how to solve this problem, It is currently not working and I think my error is in how I send the indices in my recursive step.我如何解决这个问题的实现,它目前不起作用,我认为我的错误在于我如何在递归步骤中发送索引。

#include <stdio.h>
#include <stdlib.h>


#include "assignment4.h"

int search(int arr[], int strt, int end, int value);

int search(int arr[], int strt, int end, int value) 
{ 
    int i; 
    for (i = strt; i <= end; i++) { 
        if (arr[i] == value) 
            return i; 
    } 
} 

// the function recovers the tree from its inorder and preorder
BTnode_t* reconstruct_tree(int* preorder, int* inorder, int n) {
  // implement me
    int preIndex = 0;

  BTnode_t* newnode = create_node(preorder[preIndex]);

  preIndex++;


  if( sizeof(inorder) > n-1)
    return NULL;

  if( sizeof(inorder) == n-1)
    return newnode;

  int inIndex = search( inorder, 0, n - 1, newnode->value);

  newnode->left = reconstruct_tree(preorder, inorder, inIndex -1);
  newnode->right = reconstruct_tree(preorder, inorder + inIndex +1, n-1 );

  return newnode;

}

The code used to test this part of the assignment:用于测试这部分作业的代码:

#include <stdio.h>
#include <stdbool.h>
#include <string.h>

#include "assignment4.h"



bool BT_equal(BTnode_t* t1, BTnode_t* t2) {
  if (t1 == t2)
    return true;
  if ((t1 && !t2) || (!t1 && t2))
    return false;
  return (t1->value == t2->value) && BT_equal(t1->left, t2->left) && BT_equal(t1->right, t2->right);
}

BTnode_t* create_my_tree() {
  BTnode_t* n1 = create_node(1);
  BTnode_t* n2 = create_node(2);
  BTnode_t* n3 = create_node(3);
  BTnode_t* n4 = create_node(4);
  BTnode_t* n5 = create_node(5);
  BTnode_t* n6 = create_node(6);
  BTnode_t* n7 = create_node(7);

  n1->parent = NULL;

  n1->left = n2;
  n2->parent = n1;

  n1->right = n3;
  n3->parent = n1;

  n2->left = n4;
  n4->parent = n2;
  n4->left = NULL;
  n4->right = NULL;

  n2->right = n5;
  n5->parent = n2;
  n5->left = NULL;
  n5->right = NULL;

  n3->left = n6;
  n6->parent = n3;
  n6->left = NULL;
  n6->right = NULL;

  n3->right = n7;
  n7->parent = n3;
  n7->left = NULL;
  n7->right = NULL;

  return n1;
}



bool test_q1() {
  BTnode_t* n1 = create_my_tree();

  int preorder[] = {1,2,4,5,3,6,7};
  int inorder[] = {4,2,5,1,6,3,7};
  BTnode_t* tree = reconstruct_tree(preorder, inorder, 7);

  if (BT_equal(tree, n1))  {
    printf("Q1 - ok\n");
    return true;
  }
  else {
    printf("Q1 - error\n");
    return true;
  }
}

I understand the algorithm visually.I have thought long and hard about it, and I think I am sending my indices correctly.我从视觉上理解算法。我已经思考了很长时间,并且我认为我正确地发送了我的索引。

My question: Am I making the recursive call incorrectly?我的问题:我是否错误地进行了递归调用? I think sizeof() is returning me the what sizeof(int) would return for example, how should i do this correctly?我认为 sizeof() 正在向我返回 sizeof(int) 将返回的内容,例如,我应该如何正确执行此操作? Could anyone please point me in the right direction?谁能指出我正确的方向? Or point out any glaring issues?或者指出任何明显的问题?

Thank you in advance!先感谢您!

important edit重要编辑

I got it working - here is the correct code我得到它的工作 - 这是正确的代码

BTnode_t* reconstruct_tree(int* preorder, int* inorder, int n) {
  // implement me
    static int preIndex = 0;

  BTnode_t* newnode = create_node(preorder[preIndex]);

  preIndex++;


  if (n<=1){
    return newnode;
  }

  int inIndex = search( inorder, 0, n - 1, newnode->value);

  newnode->left = reconstruct_tree(preorder, inorder, inIndex);
  newnode->right = reconstruct_tree(preorder, inorder + inIndex +1, n - inIndex -1 );

  return newnode;

}

But I still do not understand why the recursive call works, can someone please explain how the recursion is happening但我仍然不明白为什么递归调用有效,有人可以解释一下递归是如何发生的

Well, the first thing to say is that there's a simple remap on the sequences given that allows you to construct the tree from the inorder sequence, based only on the fact that the remapping only complicates things, without adding anything helpful to the problem.嗯,首先要说的是,对给定的序列有一个简单的重新映射,它允许您从中序序列构建树,仅基于重新映射只会使事情复杂化这一事实,而不会添加任何对问题有帮助的东西。 So I'll do the following simplification to the problem description:所以我将对问题描述做如下简化:

  • the preorder list is the sequence of the numbers 0... N-1 in ascending order.预排序列表是数字 0...N-1 升序排列的序列。
  • as the preorder sequence visits each node once and only once, there's a biyective mapping between the numbers of the preorder sequence and the sequence 0...N-1.由于预序序列访问每个节点一次且仅一次,因此预序序列的编号与序列 0...N-1 之间存在双向映射。
  • as the mapping is biyective, there's an inverse mapping that allows to get a new inorder sequence as a result of applying the inverse mapping to the numbers of the inorder sequence.由于映射是双向的,因此有一个逆映射允许通过将逆映射应用于中序序列的数字来获得新的中序序列。 This results in a new inorder sequence that maps to the same tree, as both sequences are equivalent now.这会产生一个映射到同一棵树的新中序序列,因为这两个序列现在是等价的。
  • this allows to forget the preorder sequence and, instead of searching for a number, to search for the minimum of the set.这允许忘记预购序列,而不是搜索数字,而是搜索集合中的最小值。
  • This solves a more general problem, but equivalent when the set of sequences of inorder nodes is compatible with a valid preordering of the preorder sequence.这解决了一个更一般的问题,但当中序节点的序列集与预序序列的有效预排序兼容时,这是等效的。

The set of preorder sequences is not always compatible with the given inorder sequence.预序序列的集合并不总是与给定的中序序列兼容。

Proof: Lets asume the building process of a tree from an inorder sequence (assuming the preorder sequence is the ordered set of numbers 0..N-1) in that case, for each node that is a root of some subtree, the minimum of the ids of that tree should be the root at the subtree... and the minimum+1 should be the subtree of its left node, except in the case of an empty left subtree.证明:假设从一个中序序列(假设前序序列是数字 0..N-1 的有序集)构建树的过程,在这种情况下,对于作为某个子树的根的每个节点,最小该树的 id 应该是子树的根......并且最小值+1 应该是其左节点的子树,除非是空的左子树。 Let's represent this fact with the following notation:让我们用以下符号表示这个事实:

                  (n)
                 /   \
[lllllll(n+1)llll]   [rrrrrrrr]

and (n+1) will only be at the right partition in case the left partition is empty:并且(n+1)只会在左分区为空的情况下位于右分区:

  (n)
 /   \
*    [rrrrrrrr(n+1)rrrrrrrrrrr]

So, in case the inorder sequence has a nonempty left subtree, and the (n+1) element in preorder is after (n) in the sequence, it will be impossible to build a tree in which the preorder sequence is valid.所以,如果中序序列有一个非空的左子树,并且前序中的第(n+1)元素在序列中的第(n)之后,就不可能构建一个前序序列有效的树。 This manifests in your code with the fact that the searched item is not present in the left subtree, being it non-empty.这体现在您的代码中,搜索项不存在于左子树中,因为它是非空的。 My solution, as it finds the minimum, just always gives a solution with a tree that is not valid preorder, but in which all the nodes have been inserted in ascending order, attaching them into an already present node.我的解决方案,因为它找到了最小值,总是给出一个树的解决方案,该树不是有效的预排序,但所有节点都已按升序插入,将它们附加到已经存在的节点中。 When that order is a valid preorder, then both algorithms give the same result.当该订单是有效的预购订单时,两种算法都会给出相同的结果。

I'll give you a solution that explores the array of inorder nodes, dividing it at the point it encounters the minimum id value (which should be the root of the subtree) and applies the algorithm again to the arrays representing the left subtree and the right subtree.我会给你一个解决方案,探索有序节点数组,在遇到最小id值(应该是子树的根)的点处划分它,并将算法再次应用于代表左子树的 arrays 和右子树。 The algorithm attaches the created node to the passed parent node, and returns the root of the matching tree:该算法将创建的节点附加到传递的父节点,并返回匹配树的根:

build.c build.c

#include <assert.h>
#include <stdlib.h>

#include "node.h"

struct node *build(const unsigned *l, int sz, struct node *parent)
{
    if (sz == 0) return NULL;

    struct node *res = malloc(sizeof *res);
    assert(res != NULL);

    int i, im = -1;
    unsigned m = ~0;
    for (i = 0; i < sz; i++) {
        const unsigned c = l[i];
        if (c < m) {
            m = c;
            im = i;
        }
    }
    assert (im >= 0 && im < sz);

    res->id     = m;
    res->parent = parent;
    res->left   = build(l, im, res);
    res->right  = build(l + im + 1, sz - im - 1, res);

    return res;
}

A whole solution or this algorithm, which prints the tree (renumbered in order to produce the valid inorder sequence that matches a valid canonical preorder one, for the same tree ---which allows to produce valid sequences of inorder/preorder datasets) is given in github .给出了一个完整的解决方案或这个算法,它打印树(重新编号以便为同一棵树生成与有效规范预序匹配的有效中序序列——允许生成中序/预序数据集的有效序列)在github中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM