【数据结构与算法】树结构篇一
来源:互联网 发布:算法入门经典训练指南 编辑:程序博客网 时间:2024/06/04 20:08
昨天又被关于树的题目给虐了。。树结构决定了递归是其一个非常重要的方法。今天重新回顾下树结构,并用题目练手。
先从树中的最长路径开始:
解题的关键在于我们要认识到最长的路径一定是某个节点下的两个最长分支的长度和。这里我们并不一定知道是哪个节点,所以可以使用 set 将每个节点的下的最长路径存储起来,最后返回最大的那个。
对于是当前节点的那个两个分支,我们可以使用 vector 存储所有分支长度,然后用 sort 排序后给出。
题目的实现可以用后序遍历的方法以时间复杂度O(N)完成。利用后序遍历算出当前节点下各分支长度。需要小心的是对于长度的定义各题有所不同,有的题是路径上的所有点个数,有的则是经过枝干的个数(即所有点个数 - 1)。
#1050 : 树中的最长路
#include <cstdio>#include <cmath>#include <set>using namespace std;#define MAXN 100010set<int> myMax;set<int>::reverse_iterator iter;struct Node{ int id; int next;}node[MAXN * 2];void Init(int N){ int i = 1, ai, bi; for(; i <= N; ++i) node[i].id = i; for(; i <= 2 * N - 1; ++i) { scanf("%d %d", &ai, &bi); node[i].id = bi; node[i].next = node[ai].next; node[ai].next = i; }}int myfindMax(int root){ int num = 0, numChild, i; set<int> nodeMax; while(node[root].next) { root = node[root].next; numChild = myfindMax(node[root].id); nodeMax.insert(numChild); } for(i = 0, iter = nodeMax.rbegin(); i < 2; ++i, ++iter) { if(iter == nodeMax.rend()) break; num += *iter; } myMax.insert(num); iter = nodeMax.rbegin(); if(nodeMax.empty()) return 1; else return 1 + *iter;}int findMax(int root){ myfindMax(root); iter = myMax.rbegin(); return *iter;}int main(){ int N; scanf("%d", &N); if(!N) { printf("%d\n", 0); return 0; } Init(N); printf("%d\n", findMax(1)); return 0;}
leetcode #104. Maximum Depth of Binary Tree
//DFS即可,注意树的深度定义也不尽相同,有的将root深度定义为0,有的为1/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */class Solution {public: int Depth = 0; void DFS(TreeNode *root) { if(root -> val > Depth) Depth = root -> val; if(root -> left) { root -> left -> val = root -> val + 1; DFS(root -> left); } if(root -> right) { root -> right -> val = root -> val + 1; DFS(root -> right); } } int maxDepth(TreeNode* root) { if(root == NULL) return 0; root -> val = 1; DFS(root); return Depth; }};
然后是树的中序遍历 leetcode #94. Binary Tree Inorder Traversal
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */class Solution {public: vector<int> inorder; void myinorder(TreeNode *root) { if(root -> left) myinorder(root -> left); inorder.push_back(root -> val); if(root -> right) myinorder(root -> right); } vector<int> inorderTraversal(TreeNode* root) { if(root == NULL) return inorder; myinorder(root); return inorder; }};
BST不同结构的数量,leetcode #96. Unique Binary Search Trees
这道题开始的思路错了,把问题给想的复杂了,其实就是一道比较简单的dp题目。关键在于要认识到一颗BST由根,左子树和右子树组成。对于一颗由 n 个节点组成的 BST,左子树与右子树分享剩下的 n - 1 个节点。如果用 num[n] 表示 n 个节点 BST 的数量,那么有这样的递归关系:
num[n] = sigma(num[i] * num[n - i - 1]) i表示左子树的节点树, n - i - 1为右子树的节点树。i : 0 ~ n - 1注意我们定义 num[0] = num[1] = 1
有了这个递推关系,代码就水到渠成:
class Solution {public: int num[1000000]; int numTrees(int n) { num[0] = num[1] = 1; int i, j; if(n == 0) return 0; if(n == 1) return 1; for(i = 2; i <= n; ++i) for(j = 0; j < i; ++j) num[i] += num[j] * num[i - j - 1]; return num[n]; }};
然后是这道题的加强版,要求输出其所有的 BST,leetcode #95. Unique Binary Search Trees II
受到上面那道题的启发,这道题也可以采用类似的方法。着手点从于根节点开始,拼接两颗之前已经得到的树作为当前根节点的左右子树(对于 N 节点的树,其树上节点值为 1 ~ N)。当前树是 BST ,所以根节点值是左子树最大值加一,右子树是将每个节点的值加上根节点的值,就能得到一颗新的 BST(也即我们正好可以利用之前得到的树)。拼接的过程需要一个树的 copy() 函数。此外我们使用二维vector来存储树根。
代码如下:
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */class Solution {public: vector<vector<TreeNode *> > ans; vector<TreeNode *> zero; //拷贝一棵树,并将每个节点的值加val TreeNode* copy(TreeNode *root, int val) { if(root == NULL) return NULL; TreeNode *p = new TreeNode(root -> val + val); p -> left = copy(root -> left, val); p -> right = copy(root -> right, val); return p; } vector<TreeNode*> generateTrees(int n) { if(n == 0) return zero; ans.push_back(vector<TreeNode *>()); ans[0].push_back(NULL); int i, j, l, r; //递推n for(i = 1; i <= n; ++i) { ans.push_back(vector<TreeNode *>()); //根节点取值 for(j = 1; j <= i; ++j) { for(l = 0; l < ans[j - 1].size(); ++l) { for(r = 0; r < ans[i - j].size(); ++r) { TreeNode *p = new TreeNode(j); p -> left = copy(ans[j - 1][l], 0); p -> right = copy(ans[i - j][r], j); ans[i].push_back(p); } } } } return ans[n]; }};
- 【数据结构与算法】树结构篇一
- 【数据结构】数据结构与算法(一)——线性结构
- 数据结构与算法之九 树结构
- 算法与数据结构(一)
- 数据结构与算法一
- 数据结构与算法一
- (一)数据结构与算法--前篇
- 数据结构与算法--二叉树(一)
- Java数据结构与算法之数据结构-逻辑结构-集合(一)------集合类简析
- 数据结构与算法系列-树-二叉树存储结构
- 数据结构与算法(一)
- 数据结构与算法笔记一
- 数据结构与算法感想一
- 【数据结构与算法】一 数组
- 数据结构与算法(一) 交换
- 数据结构与算法学习一
- 数据结构与算法-排序(一)
- 数据结构与算法笔记一
- js页面压缩上传处理源代码,仅供参考
- RESideMenu
- java中的内存分配和使用(参考学习)
- MYSQL中日期与字符串间的相互转换
- 第六节---ROS操作系统----理解ROS节点
- 【数据结构与算法】树结构篇一
- 关于xilinx cable的驱动问题
- Qt事件传递机制(QApplication::installEventFilter的作用)
- YII 获取系统级请求参数的常用方法
- Android开发-API指南-<activity>
- ios常用三方资源
- Delphi 调用 C++ 回调函数
- 使用 Java 配置进行 Spring bean 管理
- 常用的HTML特殊字符大全(css3 content)