OJ 数据结构:树——建树

根据题目要求不同,建一颗树有以下几种方式

  1. 按给定序列依次插入节点建树
    适用BST

  2. 按非叶节点的孩子节点信息建树
    适用一般树

  3. 按中序遍历序列+其他一种遍历序列建树
    适用于二叉树


给定序列插入建数

适用BST

PAT A1043
PAT A 1043

实现

//这种情况适合使用 双链表 结构
//因此结顶结构定义为
struct Node
{
  int data;
  shared_ptr<Node> left_child, right_child;
  Node(int _d = -1):
    data(_d),
    left_child(nullptr),
    right_child(nullptr)
  { }
};
//建树的过程就是不断插入节点的过程
//插入节点操作如下
void Insert(shared_ptr<Node> &root, int data)
{
  if (root == nullptr)
  {
    root = make_shared<Node>(data);
    return;
  }
  else if (data >= root->data)
  {
    Insert(root->right_child, data);
  }
  else
  {
    Insert(root->left_child, data);
  }
}
shared_ptr<Node> Create( vector<int>::iterator b, vector<int>::iterator e)
{
  shared_ptr<Node> root;
  for (auto it = b; it != e; it++)
  {
    Insert(root, *it);
  }
  return root;
}

根据孩子节点信息建树

适用所有树

这种情况一般用下标来索引节点,适合用 静态储存结构 来建树,也就是用数组
PAT A1053
PAT A 1053

实现

//使用静态储存
//用一个vector储存孩子节点的下标
struct Node
{
  int weight;
  vector<int> child;
}Tree[maxn];
//N是总节点的个数
//M是非叶节点的个数
 cin >> N >> M >> S;

//读入每个点的权值信息
  for (size_t i = 0; i < N; i++)
  {
    cin >> Tree[i].weight;
  }

//读入每个非叶节点的孩子节点信息
  while (M--)
  {
    int index, child_count;
    cin >> index >> child_count;
    for (size_t i = 0; i < child_count; i++)
    {
      int child_index;
      cin >> child_index;
      Tree[index].child.push_back(child_index);
    }
  }

根据中序+一种其他遍历序列建树

适用二叉树

根据中序遍历(inorder)的序列以及 前序(preorder)、后续(postorder)、层序(sequence order) 三者中的任一种可以建立一颗二叉树
这里写图片描述

中序+前序

算法思想
  1. 对于前序序列,第一个总是根节点,而中序序列中根节点在序列中间,将序列划分为左右两边,左边是左子树的节点,右边是右子树节点,由此可以知道左右子树的节点数
  2. 前序序列的剩余序列中,前面部分是左子树的节点,后面部分是右子树的节点
  3. 不断递归至序列长度为0
实现
//树节点定义,用双链表
struct Node
{
  int data;
  shared_ptr<Node> left_child;
  shared_ptr<Node> right_child;
  Node(int _data = -1) :
    data(_data),
    left_child(nullptr),
    right_child(nullptr)
  { }
};

//####根据前序和中序序列建树###
shared_ptr<Node> Create(int preL, int preR, int inL, int inR)
{

//序列长度为0,退出递归
  if (preL > preR)
  {
    return nullptr;
  }

//前序序列的第一位是根节点
  int root_index = preL;

  shared_ptr<Node> root(new Node(preOr[root_index]));

//在中序序列中找到根节点的位置
  int k = inL;
  for (; k <= inR; k++)
  {
    if (inOr[k] == postOr[root_index])
    {
      break;
    }
  }

//根据中序中根节点的位置划分左右子树的大小
  int num_leftTree = k - inL;
  int num_rightTree = inR - k;

//向左右子树递归
//【递归的参数容易搞错,建议画一个简易树和序列对照看】
  root->left_child = Create(preL+1 , preL+num_leftTree, inL, inL + num_leftTree - 1);
  root->right_child = Create(preR - num_rightTree , postR-num_rightTree+1, inR - num_rightTree + 1, inR);

  return root;
}

中序+后续

算法思想

和前序+中序建树一致,不同在于后续序列的最后一个节点是根节点

PAT A 1020
PAT A 1020

实现
//####根据后续和中序序列建树###
shared_ptr<Node> Create(int postL, int postR, int inL, int inR)
{

//序列长度为0,退出递归
  if (postL > postR)
  {
    return nullptr;
  }

//后续序列的最后一位是根节点
  int root_index = postR;

  shared_ptr<Node> root(new Node(postOr[root_index]));

//在中序序列中找到根节点的位置
  int k = inL;
  for (; k <= inR; k++)
  {
    if (inOr[k] == postOr[root_index])
    {
      break;
    }
  }

//根据中序中根节点的位置划分左右子树的大小
  int num_leftTree = k - inL;
  int num_rightTree = inR - k;

//向左右子树递归
  root->left_child = Create(postL , postL + num_leftTree-1 , inL, inL + num_leftTree - 1);
  root->right_child = Create(postR - num_rightTree , postR-1, inR - num_rightTree + 1, inR);

  return root;
}

中序+层序

我们都知道中序+层序可以建一颗二叉树,但算法书一般跳过这里不讲,题目中也不常见到,但原理还是相似的,实现起来也很简单。

算法思想

这里写图片描述
对照这个图讲一下算法思想

  • 中序的作用仍然是划分左右子树
  • 依次将层序序列插入一个空树

    这并不是一个BST,插入时如何知道插入的位置呢?
    答案是根据中序序列来确定
    模拟这个过程:

    1.建立一个空树 root
    2.将层序第一个A插入root,此时root是空,直接插入
    3.插入第二个 B,root不为空
    4.在中序中搜索B,发现B在其父节点(A)的左侧
    5.将B插入A的左子树
    6.直到将层序序列中所有节点插入完毕

实现
//插入函数

void Insert(shared_ptr<Node> &root, char data)
{
    if (root == nullptr)
    {
        root = make_shared<Node>(data);
        return;
    }

//根据data在中序中出现在其父节点的左边还是右边来确定插往左子树还是右子树
    auto it_data = find(begin(inOr), end(inOr), data);
    auto it_p_data = find(begin(inOr), end(inOr), root->data);
    if (it_data < it_p_data)
    {
        Insert(root->left_child, data);
    }
    else
    {
        Insert(root->right_child, data);
    }
}
shared_ptr<Node> Create(int b, int e)
{
    shared_ptr<Node> root;
    for (auto  it = b; it != e; it++)
    {
        Insert(root, seqOr[it]);
    }
    return root;
}

用中序、层序建树后,前序、后续遍历的结果
这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值