UVa 548 Tree(DFS) 根据中序和后序重建二叉树,数组实现和指针实现
来源:互联网 发布:源码上传工具 编辑:程序博客网 时间:2024/06/11 21:09
题目如下:
给一棵点带权(权值各不相同,都是小于10000的正整数)的二叉树的中序和后序遍
历,找一个叶子使得它到根的路径上的权和最小。如果有多解,该叶子本身的权应尽量小。
输入中每两行表示一棵树,其中第一行为中序遍历,第二行为后序遍历。
样例输入:
3 2 1 4 5 7 6
3 1 2 5 6 7 4
7 8 11 3 5 16 12 18
8 3 11 7 16 18 12 5
255
255
样例输出:
1
3
255
题目对于熟悉二叉树的人来说很简单,就是简单的重建然后DFS遍历找到最短的值就OK,但像我这种只有二叉树的简单概念,没有编程实现过的人来说就显得尤为困难。
根据我之前的一篇博文里面说的,有两个结论
1.后序遍历,每次递归的时候最后一个节点一定是该层的根节点
2.中序遍历,根节点的左边是左子树,右边是右子书
根据这个结论我们可以每次都找到当前递归层的根节点,然后搜索中序数组,找到对应的点,则该点左边是左子树,右边是右子树部分,很显然这是一个递归的过程,所以我们可以通过递归重建二叉树。
然后遍历一遍重建的二叉树,DFS(前序,中序,后序均可),这里要说的是,DFS是可以实现三种遍历的,因为这三种遍历本质上都是深度优先搜索,只是我们编程实现的时候输出的语句在不同的位置就实现了不同的遍历,有心的读者可以试试,代码里面注释掉了。
首先贴出指针实现的代码
// UVA548.cpp : 定义控制台应用程序的入口点。//#include <string>#include <stdio.h>#include <iostream>#include <sstream>#include <algorithm>using namespace std;const int maxv = 10000 + 10;int in_order[maxv], post_order[maxv];int len;struct Node{ Node *left; Node *right; int data; Node() { left = NULL;//注意赋初值,避免野指针 right = NULL; }};bool read_list(int* a) { string line; if (!getline(cin, line)) return false; stringstream ss(line); len = 0; int x; while (ss >> x) a[len++] = x; return len > 0;}Node *BuildTrees(Node *&T, int in[], int post[], int low1, int high1, int low2, int high2){ int i; if (low1 <= high1) { T = new Node(); T->data = post[high2]; T->left = T->right = NULL; i = low1; while (in[i] != post[high2])i++; int cnt = i - low1; BuildTrees(T->left, in, post, low1, i - 1, low2, low2 + cnt - 1); BuildTrees(T->right, in, post, i + 1, high1, low2 + cnt, high2 - 1); } return T;}void remove_tree(Node* u) { if (u == NULL) return; //提前判断比较稳妥 remove_tree(u->left); //递归释放左子树的空间 remove_tree(u->right); //递归释放右子树的空间 delete u; //调用u的析构函数并释放u结点本身的内存}int best, best_sum; //目前为止的最优解和对应的权和void dfs(Node* u, int sum) { sum += u->data; if (!u->left && !u->right) { //叶子 if (sum < best_sum || (sum == best_sum && u->data < best)) { best = u->data; best_sum = sum; } } //cout << u->data << ' ';//在这里输出节点即是前序遍历 if (u->left) { dfs(u->left, sum); } //cout << u->data << ' ';//在这里输出节点即是中序遍历 if (u->right) { dfs(u->right, sum); } //cout << u->data << ' ';//在这里输出节点即是后序遍历}int main(){ Node *root = new Node(); while (read_list(in_order)) { read_list(post_order); remove_tree(root); BuildTrees(root, in_order, post_order, 0, len - 1, 0, len - 1); best_sum = 1000000000; dfs(root, 0); cout << best << "\n"; } return 0;}
指针实现的一个弊端就是代码编写调试麻烦,可以Debug一下上面的代码,从根指针开始,一层一层的点开,点开下一层的值就看不到上一层的值,这对于我们找错来说是不利的,所以,便有了更方便调试的数组实现。
只需要将上面的Node 去掉变成int,开left 和right 还有value数组,把u->right,u->left 变成left[u],right[u]即可,但是数组实现的原理,要在纸上举个例子过几遍才能理解。
下面贴出代码
// UVA548.cpp : 定义控制台应用程序的入口点。//#include <string>#include <stdio.h>#include <iostream>#include <sstream>#include <algorithm>using namespace std;const int maxv = 10000 + 10;int in_order[maxv], post_order[maxv];int left_ch[maxv], right_ch[maxv], value[maxv];//因为权值为整数且各不相同,所以可以直接用权值作为数组下标的编号,可以节省value这个数组,//这和01背包中的二维数组优化成一维类似int len;const int root = 0;int cnt = root;void newtree() { left_ch[root] = right_ch[root] = 0; cnt= root;}int newnode() { int u = ++cnt; left_ch[u] = right_ch[u] = 0; return u;}bool read_list(int* a) { string line; if (!getline(cin, line)) return false; stringstream ss(line); len = 0; int x; while (ss >> x) a[len++] = x; return len > 0;}int BuildTrees(int &T, int in[], int post[], int low1, int high1, int low2, int high2){ int i; if (low1 <= high1) { T = newnode(); value[T] = post[high2]; i = low1; while (in[i] != post[high2])i++; int cnt = i - low1; BuildTrees(left_ch[T], in, post, low1, i - 1, low2, low2 + cnt - 1); BuildTrees(right_ch[T], in, post, i + 1, high1, low2 + cnt, high2 - 1); } return T;}int best, best_sum; //目前为止的最优解和对应的权和void dfs(int u, int sum) { sum += value[u]; if (!left_ch[u] && !right_ch[u]) { //叶子 if (sum < best_sum || (sum == best_sum && value[u] < best)) { best = value[u]; best_sum = sum; } } //cout << value[u] << ' '; //先序遍历(DFS) if (left_ch[u]) { dfs(left_ch[u], sum); } if (right_ch[u]) { dfs(right_ch[u], sum); }}int main(){ while (read_list(in_order)) { read_list(post_order); newtree(); int index = root; BuildTrees(index, in_order, post_order, 0, len - 1, 0, len - 1); //记得初始化一个值 best_sum = 1000000000; dfs(1, 0); cout << best << "\n"; } return 0;}
这里我简单地说下数组实现的原理
以这组题中的测试数据为例子:
3 2 1 4 5 7 6
3 1 2 5 6 7 4
运行完上述代码后:
index(数组下标)0 1 2 3 4 5 6 7left_ch: 0 2 3 0 0 6 0 0right_ch: 0 5 4 0 0 7 0 0 value: 0 4 2 3 1 7 5 6
根是value[1],先取出4
然后left_ch[1]为2对应value[2] = 2,right_ch[1]为5,对应value[5] = 7,所以得到
4
2 7
然后因为上述的左树2对应的索引是2,那么它的left_ch[2] = 3对应value[3] = 3,right_ch[2] = 4 对应value[4] = 1,所以树可以变为
4
2 7
3 1
7对应在value数组中的索引是5,所以7的左子数是5,右子树是6,以此即可根据数组得到原树,这里就不展开说了。这就是数组实现二叉树的方法及其数组中存的值的意义。下面提下BFS(广度优先搜索)广度优先,可以实现树的层次遍历 ,利用队列实现,下面是uva122实现广搜中的一部分。
bool bfs(vector<int> &num){ queue<int> q; num.clear(); q.push(root); while (!q.empty()) { int u = q.front(); q.pop(); if (have_value[u] == false) return false; if (left_[u] != 0)q.push(left_ch[u]); if (right_[u] != 0)q.push(right_ch[u]); num.push_back(v[u]); } return true; //输入正确}
二叉树是一种基础的数据结构,在各个公司的笔试题中经常出现,要时时温习呀。
- UVa 548 Tree(DFS) 根据中序和后序重建二叉树,数组实现和指针实现
- UVa 548 Tree 根据后序遍历和中序遍历建树后DFS
- uva 548 - Tree 二叉树重构(中序和后序)+dfs
- 根据后序和中序遍历重建二叉树
- POJ 2255Tree Recovery 二叉树重建(根据前序遍历和中序遍历写出后序遍历)
- 根据中序和后序序列重建二叉树 Construct Binary Tree from Inorder and Postorder Traversal
- 根据前序遍历和中序遍历重建二叉树的Java实现
- 根据先序和中序遍历重建二叉树java实现
- LeetCode(Construct Binary Tree from Preorder and Inorder Traversal )根据二叉树的中序遍历和后序遍历重建二叉树
- [LeetCode] 根据中序和后序序列重建二叉树
- 根据中序遍历和后序遍历重建二叉树
- 七:重建二叉树(根据先序遍历(或者后序遍历)和中序遍历重建二叉树)
- 通过前序遍历和中序遍历重建二叉树以及输出后序遍历(Java实现)
- Construct Binary Tree from Inorder and Postorder Traversal 中序和后序重建二叉树
- 数组实现根据二叉树的先序遍历和中序遍历构造二叉树
- 根据前序和中序序列重建二叉树
- 根据先序和中序遍历重建二叉树
- 根据前序和中序重建二叉树
- java面向对象三大特征 — 封装
- leetcode 4 Median of Two Sorted Arrays(二分+思路)
- 完整的HTTP请求过程
- ADO.NET 快速入门(十一):连接池
- 344.Reverse String(String-Easy)
- UVa 548 Tree(DFS) 根据中序和后序重建二叉树,数组实现和指针实现
- android studio导入项目后 编译失败 R文件丢失 R变红
- C语言概述(5)
- 如何实现一个图片懒加载库
- ADO.NET 快速入门(十二):从 SQL Server 生成 XML 数据
- Linux基础学习笔记之如何使vm下centos启动后就自动以root身份登录系统
- C#学习笔记—值类型与引用类型
- numpy的基本用法(五)——numpy array分割
- ADO.NET 快速入门(十三):使用 OLE DB 检索数据