《Cracking the Coding Interview》——第4章:树和图——题目7
2014-03-19 04:48
题目:最近公共父节点问题。
解法1:Naive算法,先对其高度,然后一层一层往上直到找到结果。
代码:
// 4.7 Least Common Ancestor
// This solution is Naive Algorithm, may timeout on very large and skewed trees.
#include <cstdio>
#include <cstring>
using namespace std; const int MAXN = ;
// tree[x][0]: parent of node x
// tree[x][1]: left child of node x
// tree[x][2]: right child of node x
// tree[x][3]: value of node x
int tree[MAXN][];
// number of nodes
int e; void build(int a)
{
int tmp; scanf("%d", &tmp);
if(tmp)
{
tree[a][] = e;
tree[e][] = tmp;
tree[e][] = a;
++e;
// build the left subtree
build(e - );
} scanf("%d", &tmp);
if(tmp)
{
tree[a][] = e;
tree[e][] = tmp;
tree[e][] = a;
++e;
// build the right subtree
build(e - );
}
} int main()
{
int n, ni;
int i;
// the value to be queried
int m1, m2;
// the corresponding node indices of m1 and m2
int s1, s2;
int t1, t2;
int c1, c2, c; while (scanf("%d", &n) == ) {
for (ni = ; ni < n; ++ni) {
// get value for root node
e = ;
scanf("%d", &tree[][]); // root has no parent node
tree[][] = -;
build(); while (scanf("%d%d", &m1, &m2) == && (m1 && m2)) {
s1 = s2 = -;
for (i = ;i <= e; ++i) {
if (tree[i][] == m1) {
s1 = i;
// there're duplicate values
break;
}
}
for (i = ;i <= e; ++i) {
if (tree[i][] == m2) {
s2 = i;
// there're duplicate values
break;
}
} if (s1 != - && s2 != -) {
t1 = s1;
t2 = s2;
c1 = c2 = ; // c1 is the depth of t1
while (tree[t1][] != -) {
++c1;
t1 = tree[t1][];
}
// c2 is the depth of t2
while (tree[t2][] != -) {
++c2;
t2 = tree[t2][];
} // move'em to the same height level
if (c1 > c2) {
c = c1 - c2;
while(c--) {
s1 = tree[s1][];
}
} else {
c = c2 - c1;
while(c--) {
s2 = tree[s2][];
}
} while(s1 != s2)
{
s1 = tree[s1][];
s2 = tree[s2][];
}
printf("%d\n", tree[s1][]);
} else {
// At least one value is not found in the tree.
printf("Not found in the tree.\n");
}
}
}
} return ;
}
解法2:Naive算法的倍增优化。
代码:
// 4.7 Least Common Ancestor
// O(log(n)) solution with binary decomposition.
#include <cstdio>
#include <cstring>
using namespace std; typedef struct st{
public:
int key;
st *ll;
st *rr;
st(int _key = ): key(_key), ll(NULL), rr(NULL) {}
}st; // maximal number of nodes
const int MAXN = ;
// key -> node_index mapping
int hash_key[MAXN];
// node_index -> key mapping
int key_hash[MAXN];
// depth of each node
int depth[MAXN];
// array recording ancestors
int anc[MAXN][];
// total number of nodes, index starting from 1
int nc; // recursively calculate depths of nodes
void getDepth(st *root, int dep)
{
if (root == NULL) {
return;
}
depth[hash_key[root->key]] = dep;
if (root->ll != NULL) {
getDepth(root->ll, dep + );
}
if (root->rr != NULL) {
getDepth(root->rr, dep + );
}
} // recursively construct a binary tree
void constructBinaryTree(st *&root)
{
int tmp; scanf("%d", &tmp);
if (tmp == ) {
root = NULL;
} else {
root = new st(tmp);
++nc;
if (hash_key[tmp] == ) {
hash_key[tmp] = nc;
}
key_hash[nc] = tmp;
constructBinaryTree(root->ll);
constructBinaryTree(root->rr);
}
} // recursively initialize ancestor array
void getParent(st *root)
{
if (root == NULL) {
return;
} // anc[x][0] is the direct parent of x.
if (root->ll != NULL) {
anc[hash_key[root->ll->key]][] = hash_key[root->key];
getParent(root->ll);
}
if (root->rr != NULL) {
anc[hash_key[root->rr->key]][] = hash_key[root->key];
getParent(root->rr);
}
} // calculate LCA in O(log(n)) time
int leastCommonAncestor(int x, int y)
{
int i; if (depth[x] < depth[y]) {
return leastCommonAncestor(y, x);
}
for (i = ; i >= ; --i) {
if (depth[anc[x][i]] >= depth[y]) {
x = anc[x][i];
if (depth[x] == depth[y]) {
break;
}
}
}
if (x == y) {
return x;
} for (i = ; i >= ; --i) {
if (anc[x][i] != anc[y][i]) {
// they'll finally be equal, think about the reason.
x = anc[x][i];
y = anc[y][i];
}
} // this is the direct parent of x
return anc[x][];
} st *deleteTree(st *root)
{
if (NULL == root) {
return NULL;
} if (root->ll != NULL) {
root->ll = deleteTree(root->ll);
}
if (root->rr != NULL) {
root->rr = deleteTree(root->rr);
}
delete root;
root = NULL; return NULL;
} int main()
{
int ci, cc;
int i, j;
int x, y;
st *root; while (scanf("%d", &cc) == ) {
for (ci = ; ci < cc; ++ci) {
// data initialization
memset(hash_key, , MAXN * sizeof(int));
memset(key_hash, , MAXN * sizeof(int));
memset(depth, , MAXN * sizeof(int));
memset(anc, , MAXN * * sizeof(int));
nc = ;
root = NULL; constructBinaryTree(root);
getParent(root);
getDepth(root, );
for (j = ; j < ; ++j) {
for (i = ; i <= nc; ++i) {
anc[i][j] = anc[anc[i][j - ]][j - ];
}
}
while (scanf("%d%d", &x, &y) == && (x && y)) {
if (hash_key[x] == || hash_key[y] == ) {
printf("Not found in the tree.\n");
} else {
printf("%d\n", key_hash[leastCommonAncestor(hash_key[x], hash_key[y])]);
}
} root = deleteTree(root);
}
} return ;
}
解法3:Tarjan离线算法,并查集的妙用。
代码:
// 4.7 Least Common Ancestor
// Tarjan Offline Algorithm
#include <cstdio>
#include <cstdlib>
#include <unordered_map>
#include <unordered_set>
using namespace std; struct TreeNode {
int val;
TreeNode *left;
TreeNode *right; TreeNode(int _val = ): val(_val), left(nullptr), right(nullptr) {};
}; void constructTree(vector<TreeNode *> &nodes,
unordered_set<TreeNode *> &checked)
{
int ll, rr;
int i; for (i = ; i < (int)nodes.size(); ++i) {
checked.insert(nodes[i]);
}
for (i = ; i < (int)nodes.size(); ++i) {
scanf("%d%d", &ll, &rr);
if (ll > ) {
nodes[i]->left = nodes[ll - ];
checked.erase(nodes[ll - ]);
}
if (rr > ) {
nodes[i]->right = nodes[rr - ];
checked.erase(nodes[rr - ]);
}
}
} TreeNode *findRoot(TreeNode *node, unordered_map<TreeNode *, TreeNode *> &disjoint_set)
{
if (node != disjoint_set[node]) {
disjoint_set[node] = findRoot(disjoint_set[node], disjoint_set);
} return disjoint_set[node];
} void tarjanLCA(TreeNode *root, unordered_map<TreeNode *, TreeNode *> &disjoint_set,
unordered_map<TreeNode *, unordered_map<TreeNode *, TreeNode *> > &query,
unordered_set<TreeNode *> &checked)
{
if (root == nullptr) {
return;
} disjoint_set[root] = root;
if (root->left != nullptr) {
tarjanLCA(root->left, disjoint_set, query, checked);
disjoint_set[root->left] = root;
}
if (root->right != nullptr) {
tarjanLCA(root->right, disjoint_set, query, checked);
disjoint_set[root->right] = root;
}
checked.insert(root); if (query.find(root) == query.end()) {
return;
} unordered_map<TreeNode *, TreeNode *>::iterator it;
for (it = query[root].begin(); it != query[root].end(); ++it) {
if (it->second != nullptr) {
// already solved, skip it
continue;
}
if (checked.find(it->first) != checked.end()) {
query[root][it->first] = query[it->first][root] = findRoot(it->first, disjoint_set);
}
}
} int main()
{
int n;
int i;
int val;
vector<TreeNode *> nodes;
TreeNode *root;
unordered_map<TreeNode *, TreeNode *> disjoint_set;
unordered_map<TreeNode *, unordered_map<TreeNode *, TreeNode *> > query;
unordered_map<TreeNode *, unordered_map<TreeNode *, TreeNode *> >::iterator it1;
unordered_set<TreeNode *> checked;
unordered_map<TreeNode *, TreeNode *>::iterator it2;
int idx1, idx2; while (scanf("%d", &n) == && n > ) {
nodes.resize(n);
for (i = ; i < n; ++i) {
scanf("%d", &val);
nodes[i] = new TreeNode(val);
}
constructTree(nodes, checked);
root = *(checked.begin());
checked.clear(); while (scanf("%d%d", &idx1, &idx2) == && (idx1 >= && idx2 >= )) {
if (idx1 > idx2) {
i = idx1;
idx1 = idx2;
idx2 = i;
}
query[nodes[idx1]][nodes[idx2]] = nullptr;
query[nodes[idx2]][nodes[idx1]] = nullptr;
}
// Tarjan Offline Algorithm
tarjanLCA(root, disjoint_set, query, checked); // print the results
for (it1 = query.begin(); it1 != query.end(); ++it1) {
for (it2 = (it1->second).begin(); it2 != (it1->second).end(); ++it2) {
if (it1->first->val > it2->first->val) {
continue;
}
printf("The least common ancestor of %d and %d is %d.\n",
it1->first->val, it2->first->val, it2->second->val);
}
} // clear up
disjoint_set.clear();
checked.clear();
for (it1 = query.begin(); it1 != query.end(); ++it1) {
(it1->second).clear();
}
query.clear();
} return ;
}
《Cracking the Coding Interview》——第4章:树和图——题目7的更多相关文章
- Cracking the coding interview 第一章问题及解答
Cracking the coding interview 第一章问题及解答 不管是不是要挪地方,面试题具有很好的联系代码总用,参加新工作的半年里,做的大多是探索性的工作,反而代码写得少了,不高兴,最 ...
- 《Cracking the Coding Interview》读书笔记
<Cracking the Coding Interview>是适合硅谷技术面试的一本面试指南,因为题目分类清晰,风格比较靠谱,所以广受推崇. 以下是我的读书笔记,基本都是每章的课后习题解 ...
- Cracking the coding interview
写在开头 最近忙于论文的开题等工作,还有阿里的实习笔试,被虐的还行,说还行是因为自己的水平或者说是自己准备的还没有达到他们所需要人才的水平,所以就想找一本面试的书<Cracking the co ...
- Cracking the Coding Interview(Trees and Graphs)
Cracking the Coding Interview(Trees and Graphs) 树和图的训练平时相对很少,还是要加强训练一些树和图的基础算法.自己对树节点的设计应该不是很合理,多多少少 ...
- Cracking the coding interview目录及资料收集
前言 <Cracking the coding interview>是一本被许多人极力推荐的程序员面试书籍, 详情可见:http://www.careercup.com/book. 第六版 ...
- Cracking the Coding Interview(Stacks and Queues)
Cracking the Coding Interview(Stacks and Queues) 1.Describe how you could use a single array to impl ...
- 二刷Cracking the Coding Interview(CC150第五版)
第18章---高度难题 1,-------另类加法.实现加法. 另类加法 参与人数:327时间限制:3秒空间限制:32768K 算法知识视频讲解 题目描述 请编写一个函数,将两个数字相加.不得使用+或 ...
- Cracking the Coding Interview 150题(二)
3.栈与队列 3.1 描述如何只用一个数组来实现三个栈. 3.2 请设计一个栈,除pop与push方法,还支持min方法,可返回栈元素中的最小值.pop.push和min三个方法的时间复杂度必须为O( ...
- 《Cracking the Coding Interview》——第4章:树和图——题目9
2014-03-19 05:07 题目:给定一棵二叉树T和一个值value,在T中找出所有加起来和等于value的路径.路径的起点和终点都可以是树的任意节点. 解法:我偷了个懒,直接把这棵树看成一个无 ...
- 《Cracking the Coding Interview》——第4章:树和图——题目8
2014-03-19 05:04 题目:给定两棵二叉树T1和T2,判断T2是否是T1的子树.子树的定义是,以T1的某个节点(可以是T1的根)作为根节点,得到的这棵树和T2一模一样. 解法:首先可以根据 ...
随机推荐
- LeetCode OJ Palindrome Number(回文数)
class Solution { public: bool isPalindrome(int x) { ,init=x; ) return true; ) return false; ){ r=r*+ ...
- JSON:使用json_encode函数解析结果为Null
1.首先,数据库中的json数据是这样的 2.仓鼠使用json_encode()函数进行解析json数据时,显示了一个NULL: 3.这时候,我们需要使用,表示在解析json之前,该json是有语法错 ...
- 使用g++ 编译C++程序
在命令行下,编译C++程序 g++ main.cpp -o main.exe
- mysql的慢查询实战+sql优化
背景:使用A电脑安装mysql,B电脑通过xshell方式连接,数据内容我都已经创建好,现在我已正常的进入到mysql中 步骤1:设置慢查询日志的超时时间,先查看日志存放路径查询慢日志的地址,因为有慢 ...
- Codeforces 760B Frodo and pillows
题目链接:http://codeforces.com/problemset/problem/760/B 题意:n个床位,m个枕头,第k个位置最多有多少个枕头,其中相邻之间的差<=1; 第k个位置 ...
- BFS变换素数,POJ(3126)
题目链接:http://poj.org/problem?id=3126 解题报告: #include <iostream> #include <queue> #include ...
- vue中css动画原理
显示原理: <transition name='fade'> <div v-if='show'>hello world</div> </transition& ...
- 2018.8.2 Juint测试介绍及其命名的规范
JUnit - 测试框架 什么是 Junit 测试框架? JUnit 是一个回归测试框架,被开发者用于实施对应用程序的单元测试,加快程序编制速度,同时提高编码的质量.JUnit 测试框架能够轻松完成以 ...
- 详解LinkedHashMap如何保证元素迭代的顺序
大多数情况下,只要不涉及线程安全问题,Map基本都可以使用HashMap,不过HashMap有一个问题,就是迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序.HashMap的这一缺点 ...
- CentOS 系统 Docker 的命令大全
本文记录 CentOS 系统 Docker 的命令大全 命令 docker ps 显示运行中的容器 docker ps -a 显示所有容器 docker start 容器名称 启动容器 docker ...